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)
}
}
}
}
@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.
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.
@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.
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.