My problem is phrased in the code below. I'm trying to get some input that has the .map function in it. I know that if I call .map to it, it will return an Int to me.
// In my case, they are different representations of Ints
// By that I mean that in the end it all boils down to Int
val list: Seq[Int] = Seq(1,2,3,4)
val optInt: Option[Int] = Some(1)
// I can use a .map with a Seq, check!
list.map {
value => println(value)
}
// I can use it with an Option, check!
optInt.map {
value => println(value)
}
// Well, you're asking yourself why do I have to do it,
// Why don't I use foreach to solve my problem. Check!
list.foreach(println)
optInt.foreach(println)
// The problem is that I don't know what I'm going to get as input
// The only thing I know is that it's "mappable" (it has the .map function)
// And that if I were to apply .map it would return Ints to me
// Like this:
def printValues(genericInputThatHasMap: ???) {
genericInputThatHasMap.map {
value => println(value)
}
}
// The point is, what do I have to do to have this functionality?
// I'm researching right now, but I still haven't found anything.
// That's why I'm asking it here =(
// this works:
def printValues(genericInputThatHasMap: Seq[Int]) {
genericInputThatHasMap.map {
value => println(value)
}
}
Thanks in advance! Cheers!
First for a quick note about map
and foreach
. If you're only interested in performing an operation with a side effect (e.g., printing to standard output or a file, etc.) on each item in your collection, use foreach
. If you're interested in creating a new collection by transforming each element in your old one, use map
. When you write xs.map(println)
, you will in fact print all the elements of the collection, but you'll also get back a (completely useless) collection of units, and will also potentially confuse future readers of your code—including yourself—who expect foreach
to be used in a situation like this.
Now on to your problem. You've run into what is in my opinion one of the ugliest warts of the Scala standard library—the fact that methods named map
and foreach
(and flatMap
) get magical treatment at the language level that has nothing to do with a specific type that defines them. For example, I can write this:
case class Foo(n: Int) {
def foreach(f: Int => Unit) {
(0 until n) foreach f
}
}
And use it in a for
loop like this, simply because I've named my method foreach
:
for (i <- Foo(10)) println(i)
You can use structural types to do something similar in your own code:
def printValues(xs: { def foreach(f: (Int) => Unit): Unit }) {
xs foreach println
}
Here any xs
with an appropriately typed foreach
method—for example an Option[Int]
or a List[Int]
—will compile and work as expected.
Structural types get a lot messier when you're trying to work with map
or flatMap
though, and are unsatisfying in other ways—they impose some ugly overhead due to their use of runtime reflection, for example. They actually have to be explicitly enabled in Scala 2.10 to avoid warnings for these reasons.
As senia's answer points out, the Scalaz library provides a much more coherent approach to the problem through the use of type classes like Monad
. You wouldn't want to use Monad
, though, in a case like this: it's a much more powerful abstraction than you need. You'd use Each
to provide foreach
, and Functor
for map
. For example, in Scalaz 7:
import scalaz._, Scalaz._
def printValues[F[_]: Each](xs: F[Int]) = xs foreach println
Or:
def incremented[F[_]: Functor](xs: F[Int]) = xs map (_ + 1)
To summarize, you can do what you want in a standard, idiomatic, but arguably ugly way with structural types, or you can use Scalaz to get a cleaner solution, but at the cost of a new dependency.
My thoughts on the two approaches.
You can use a structural type for foreach
, but for map
it doesn't appear you can construct one to work across multiple types. For example:
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 commented out above, it has List
hardcoded as a type parameter in the CanBuildFrom
implicit. There might be a way to pick up the type generically - I will leave that as a question to the Scala type gurus out there. I tried substituting HasMapAndForeach
and this.type
for List
but neither of those worked.
The usual performance caveats about structural types apply.
Since structural types is a dead end if you want to support map
then let's look at the scalaz approach from Travis 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 below correct me if I am wrong, I am using this as a scalaz learning experience)
The typeclasses Each
and Functor
are used to restrict the types of F
to ones where implicits are available for Each[F]
or Functor[F]
, respectively. For example, in the call
printValues(List(1,2,3))
the compiler will look for an implicit that satisfies Each[List]
. The Each
trait is
trait Each[-E[_]] {
def each[A](e: E[A], f: A => Unit): Unit
}
In the Each
object there is an implicit for Each[TraversableOnce]
(List
is a subtype of TraversableOnce
and the trait 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 "context bound" syntax
def printValues[F[_]: Each](xs: F[Int])
is shorthand for
def printValues(xs: F[Int])(implicit ev: Each[F])
Both of these denote that F
is a member of the Each
typeclass. The implicit that satisfies the typeclass is passed as the ev
parameter to the printValues
method.
Inside the printValues
or incremented
methods the compiler doesn't know that xs
has a map
or foreach
method because the type parameter F
doesn't have any upper or lower bounds. As far as it can tell F
is AnyRef
and satisfies the context bound (is part of the typeclass). What is in scope that does have foreach
or map
? MA
from scalaz has both 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
are constrained by the Each
or Functor
typeclass. These are the same constraints from the original methods so the constraints are satisfied and an implicit conversion to MA[F, Int]
takes place via 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
}
}
The type F
in the original method becomes type M
in MA
.
The implicit parameter that was passed into the original call is then passed as the implicit parameter into foreach
or map
. In the case of foreach
, each
is called on its implicit parameter e
. In the example from above the implicit ev
was type Each[TraversableOnce]
because the original parameter was a List
, so e
is the same type. foreach
calls each
on e
which delegates to foreach
on TraversableOnce
.
So the order of calls for printValues(List(1,2,3))
is:
new Each[TraversableOnce]
-> printValues
-> new MA
-> MA.foreach
-> Each.each
-> TraversableOnce.foreach
As they say, there is no problem that can't be solved with an extra level of indirection :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With