My thoughts are on two approaches.
Structural types
You can use the structural type for foreach , but it is not displayed for the map , you can create it to work with several types. For instance:
import collection.generic.CanBuildFrom object StructuralMap extends App { type HasMapAndForeach[A] = { // def map[B, That](f: (A) โ B)(implicit bf: CanBuildFrom[List[A], B, That]): That def foreach[B](f: (A) โ B): Unit } def printValues(xs: HasMapAndForeach[Any]) { xs.foreach(println _) } // def mapValues(xs: HasMapAndForeach[Any]) { // xs.map(_.toString).foreach(println _) // } def forComp1(xs: HasMapAndForeach[Any]) { for (i <- Seq(1,2,3)) println(i) } printValues(List(1,2,3)) printValues(Some(1)) printValues(Seq(1,2,3)) // mapValues(List(1,2,3)) } scala> StructuralMap.main(new Array[String](0)) 1 2 3 4 5 6 7 8 9 10
See the map method described above, it has List hardcoded as a type parameter in CanBuildFrom implicit. Perhaps there is a way to get the type as a whole - I will leave this as a question to a guru like Scala. I tried replacing HasMapAndForeach and this.type with List , but none of them worked.
The usual recommendations for structural types apply.
Scalaz
Since structural types are dead ends, if you want to support map , then let's take a look at Travis's scalaz approach and see how it works. Here are his methods:
def printValues[F[_]: Each](xs: F[Int]) = xs foreach println def incremented[F[_]: Functor](xs: F[Int]) = xs map (_ + 1)
(In the following, correct to me, if I am mistaken, I use this as an experience in studying skazaz)
The type tables Each and Functor are used to restrict the types F to those where implications are available for Each[F] or Functor[F] respectively. For example, in a call
printValues(List(1,2,3))
the compiler will look for an implicit one that satisfies Each[List] . Each value
trait Each[-E[_]] { def each[A](e: E[A], f: A => Unit): Unit }
In the Each object, there is an implicit expression for Each[TraversableOnce] ( List is a subtype of TraversableOnce , and the flag is contravariant):
object Each { implicit def TraversableOnceEach[A]: Each[TraversableOnce] = new Each[TraversableOnce] { def each[A](e: TraversableOnce[A], f: A => Unit) = e foreach f } }
Note that the syntax is โrelated contextโ
def printValues[F[_]: Each](xs: F[Int])
is short for
def printValues(xs: F[Int])(implicit ev: Each[F])
Both of them mean that F is a member of the class Each . An implicit one that satisfies the typeclass class is passed as an ev parameter to the printValues method.
Inside the printValues or incremented methods, the compiler does not know that xs has a map or foreach method, since a parameter of type F has no upper or lower bounds. As far as he can tell, F is AnyRef and satisfies the context binding (is part of the class). What is a foreach or map scope? MA scalaz MA has foreach and map methods:
trait MA[M[_], A] { def foreach(f: A => Unit)(implicit e: Each[M]): Unit = e.each(value, f) def map[B](f: A => B)(implicit t: Functor[M]): M[B] = t.fmap(value, f) }
Note that the foreach and map methods on MA limited to the Each or Functor class. These are the same constraints as the original methods, so the constraints are satisfied, and the implicit conversion to MA[F, Int] occurs using the maImplicit method:
trait MAsLow extends MABLow { implicit def maImplicit[M[_], A](a: M[A]): MA[M, A] = new MA[M, A] { val value = a } }
Type F in the original method becomes type M in MA .
The implicit parameter that was passed to the original call is then passed as an implicit parameter to foreach or map . In the case of foreach , Each is invoked by the implicit parameter e . In the example above, the implicit ev was type Each[TraversableOnce] , because the original parameter was List , so e is the same type. foreach calls Each on e , which delegates foreach to TraversableOnce .
So the call order for printValues(List(1,2,3)) :
new Each[TraversableOnce] โ printValues โ new MA โ MA.foreach โ Each.each โ TraversableOnce.foreach
As they say, there is no problem that cannot be solved with an additional level of indirection :)