20

I'm curious - what is the point of generic type U in declaration of Traversable's foreach method?

def foreach[U](f: A => U): Unit

Since return type of Function1 is covariant, why can't that be just:

def foreach(f: A => Any): Unit

?

ghik
  • 10,706
  • 1
  • 37
  • 50
  • I don't have time to come up with an example, but I strongly suspect it is to let the type inferencer ignore `foreach` when it comes to inferring the return type of a function. But I don't know a case off the top of my head where it would matter (i.e. that `Any` wouldn't result in the same inference). – Rex Kerr Dec 30 '12 at 23:47

2 Answers2

10

Not being Martin Odersky, I can only guess :-) Looking at the Scaladoc of foreach, I see this:

  /** Applies a function `f` to all elements of this $coll.
   *
   *  @param  f   the function that is applied for its side-effect to every element.
   *              The result of function `f` is discarded.
   *              
   *  @tparam  U  the type parameter describing the result of function `f`. 
   *              This result will always be ignored. Typically `U` is `Unit`,
   *              but this is not necessary.
   *
   *  @usecase def foreach(f: A => Unit): Unit
   */

So the return type of f doesn't matter and its result is always discarded. This, to me, suggests that using a generic type parameter here to mark the return type is just a documentation subtlety, saying "the return type can be anything, really anything, you like". Whereas a return type of Any may suggest to (some) readers some sort of limitation to the function types applicable here.

Another aspect is that Scala was very consciously designed to be generic from the ground up. So - to me - using a generic type parameter here is consistent with the general philosophy of the language, while using Any - although technically usable - would be a definitely non-generic approach which would be at odds with the rest of the language.

Péter Török
  • 114,404
  • 31
  • 268
  • 329
-5

Maybe to allow you inherit from a Traversable, and make some use of return value U from f: A => U?

trait TraversableWithHandler[+A, E <: Option[Throwable]] extends Traversable[A] {
  override def foreach[U](f: A => U): Unit
  def handleError(t: E) 
}

For example in jQuery, return of false from inside foreach is equivalent to break, any different value is a continue.

Use Case

breakable { 
  List(1,2,3).foreach(_ match { 
    case 1 => println("ok")
    case 2 => 
      println("Sort of, soft interrupt?")
      return false
    case 3 => break
  })
}

Because the next code (parallel), never breaks (stackless throwable solution seems non-ideal in this case?):

import scala.util.control.Breaks._

breakable { 
  (0 to 100).toList.par.foreach(_ match { 
    case n if (n <= 50) => 
      println("#" * 100)
      try { break } catch { 
        case t: Throwable => println("" + t); break 
      }
    case n if (n > 50) => 
      println("" + n)
    case _ => "ok" 
  }) 
}
idonnie
  • 1,703
  • 12
  • 11
  • -1: Given that stackless exceptions are used for control flow in such situations anyway, and that having a formal return value is of no use in such a case, this is _highly_ unlikely. Plus, you don't actually show any use cases. I have no idea of how to use the signature above. – Rex Kerr Dec 30 '12 at 21:20
  • This would be in contrast with the Scaladoc which says: "The result of function `f` is discarded." – Péter Török Dec 30 '12 at 21:20
  • 6
    This answer isn't just wrong, it's potentially very confusing. You've just renamed the type parameter so that it shadows `scala.Boolean`—you definitely haven't somehow constrained the method declared in `Traversable`. – Travis Brown Dec 30 '12 at 22:06
  • I've updated the answer, it seems to me that returning some value from `foreach`, in a case of parallel collection - could be quite useful? – idonnie Dec 30 '12 at 22:32
  • 2
    You can't do anything with a `[U]` return value that you can't with an `Any`, and calling something `foreach` that doesn't actually apply the function to every element of the collection is asking for confusion at best. – Rex Kerr Dec 30 '12 at 23:11
  • That shadowing... What a fat bug. Function that does not actually applied to every element is not a big deal - that's a function with an exception. In a case of a parallel collection, `foreach` have such a behavior, as if it wraps code with `try ... catch`, and then returns `Either[Unit, Throwable]` as a result of execution on each step - that's confusing. Why not somehow allow a user of a framework, to control those 2 kinds of `Traversable`'s behavior. – idonnie Dec 31 '12 at 00:28
  • For example, handle a return value of `Throwable` differently - so I will just wrap my sequential and parallel functions using the same wrappers. That wrappers could be `implicit`... As in Akka - explicitly manipulate possible failures, etc. – idonnie Dec 31 '12 at 00:45
  • 1
    You _still_ have shadowing bugs, this time with `E`, and if you don't shadow `E`, you fail to obey the Liskov Substitution Principle for `foreach` by restricting the kind of function it can take. – Rex Kerr Dec 31 '12 at 14:26
  • My point is that error handling in sequential and parallel `Traversable`-s are different, and that affects actual traversing process. This difference could be expressed with a method like, `def handleError(t: E): Option[E]` where `E <: Throwable`. Return value from inside `foreach` method, could have a practical application, but not in Scala =) Also, break is broken for `.par`, I think. – idonnie Dec 31 '12 at 19:04