Help understanding compiler errors implementing foreach method

I’m trying to implement the foreach method for my class, and I would like to ask for help understanding the compiler messages. I get different messages from IntelliJ and the compiler.

object MyComprehension {

  case class Tail[A<:List[Any]](initialList:A) {
    def foreach(block: A => Unit): Unit = {
      initialList match {
        case Nil => Unit
        case _ :: tail => {
          block // evaluated for side effect
          Tail(tail).foreach(block)
        }
      }
    }
  }

  def main(argv:Array[String]):Unit = {

  }
}

Here are the errors. It is confusing to me that the compiler says it wants an A=>Unit, and I’ve given it a List[Any]=>Unit. But in my declaration didn’t I tell it that an A=>Unit is a List[Any]=>Unit?
Do I simply need somehow to answer the covariance/contravarience question? I tried changing A to +A and to -A in the declaration, but that doesn’t seem to solve the problem.

Error:(8, 14) pattern type is incompatible with expected type;
 found   : scala.collection.immutable.Nil.type
 required: A
        case Nil => Unit
Error:(11, 30) type mismatch;
 found   : A => Unit
 required: List[Any] => Unit
          Tail(tail).foreach(block)
Warning:(10, 11) a pure expression does nothing in statement position; multiline expressions might require enclosing parentheses
          block

I also tried the following, but was met with different errors.

object MyComprehension {

  case class Tail[List[A]](initialList:List[A]) {
    def foreach(block: List[A] => Unit): Unit = {
      initialList match {
        case Nil => Unit
        case _ :: tail => {
          block // evaluated for side effect
          Tail(tail).foreach(block)
        }
      }
    }
  }

  def main(argv:Array[String]):Unit = {

  }
}

Here is the first of many errors

Error:(5, 45) not found: type A
  case class Tail[List[A]](initialList:List[A]) {

What do you want A to be, a subtype of List[Any] (as in your first example) or any type and have a list of A's (as in your second example)?
If the latter you should write:

	case class Tail[A](initialList:List[A]) {
		def foreach(block: List[A] => Unit): Unit = {
			initialList match {
				case Nil => Unit
				case _ :: tail => {
					block // evaluated for side effect
					Tail(tail).foreach(block)
				}
			}
		}
	}

Which compiles just fine

1 Like

The problem is that the A in a function A => B is contravariant, so if AA is a subtype of A, it does not follow that AA => B is a subtype of A => B.

In your case, although A is a subtype of List[Any], A => Unit is not a subtype of List[Any] => Unit.

Why do you need the parameter A? Can’t you just use List[Any]?

1 Like

@curoli, I have to think a bit more about what it is I’m trying to accomplish. I’m trying to make an object which I can initialize as obj=Tail(1,2,3,4) and thereafter that obj.map(f) accumulates the values of Tail(f(Tail(1,2,3,4)),f(Tail(2,3,4)),f(Tail(3,4)),f(Tail(4))) or Tail(f(List(1,2,3,4)),f(List(2,3,4)),f(List(3,4)),f(List(4))), not sure which. So what I’m trying to figure out is what would the value of obj.flatMap(g) need to be? Would it be something akin to g(Tail(1,2,3,4)) ++ g(Tail(2,3,4)) ++ g(Tail(3,4)) ++ g(Tail(4)). And can that be correct made to work correctly as a monad? When I just think about it, it works. But when I try to code it the types don’t match.

Did you also consider using List's tails method?

So if I understand tails correctly, then something like mappableTails (below) would indeed give me the list of values I want. But I was trying to understand (convince myself yes or no) whether it is possible to create a monad which iterates through the cons cells of a list rather than iterating through the heads of the cons cells.

def mappableTails(list:List[A]):List[List[A]] => {
  list match {
     case Nil => Nil
     case head::tail => list :: list.tails.reverse.tail.reverse
  }
}

Note that this strategy requires allocating a list of length N 3 times, just to iterate over the tails of a given list of length N. I’d generally prefer to avoid such allocation, as it seems unnecessary. All the elements I want (the tails of the given list) are already allocated.

Sorry I haven’t tested this code. so maybe there’s a mistake, but I hope you understand my point.

.reverse.tail.reverse can be shortened to .init.

1 Like

@jimka @andreak Did you forget to pass tail to block? You should write block(tail).
Also, I’d write () instead of Unit. Apart from this I like Andreas’ suggestion:

case class Tail[A](initialList: List[A]) {
  def foreach(block: List[A] => Unit): Unit = initialList match {
    case Nil => ()
    case _ :: tail =>
      block(tail)
      Tail(tail).foreach(block)
  }
}

Btw., list.tails is an Iterator so it does not allocate a new list.

Oh, good catch. @jimka, note that Unit is the type, () is the value of that type.

Even though list.tails is an iterator, it is not the list of cons cells. It is missing list itself, and it contains Nil which is not a cons cell. Is there a way to remove the final element of list.tails without converting to a list? remember we have to effectively iterate over list::(list.tails.reverse.tail.reverse)

For example is list::list.tails.init still an iterator which will know to skip the final element?

@PeterE, good catch, but I think we have to evaluate block on initialList rather than tail. Why? Because otherwise it is called the final time on Nil which is not a cons, and also block never has access to the first element of initialList. This is a case where having the correct types does not imply the correct semantics.

case class Tail[A](initialList: List[A]) {
  def foreach(block: List[A] => Unit): Unit = initialList match {
    case Nil => ()
    case _ :: tail =>
      block(initailList)
      Tail(tail).foreach(block)
  }
}

Even though list.tails is an iterator, it is not the list of cons cells. It is missing list itself, and it contains Nil which is not a cons cell. Is there a way to remove the final element of list.tails without converting to a list?

List(1,2,3).tails.filter(_ ne Nil) is still an iterator. Doesn’t it contain the list itself?

Also note that list.tails.init or reverse do not work since init/reverse are methods of List but not of Iterator.

Interesting, however it would be possible (would have been possible) to implement .init as an iterator; would just have to know to skip the final element. hmm. perhaps a few other things like, it would need to be able to calculate its size correctly.