Is there a way to find out through inheritance (or otherwise) when a class defines a .map function in Scala?

My problem is formulated in the code below. I am trying to get some input that has a .map function in it. I know that if I call .map, it will return Int for 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 "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 why I'm asking it here =( // this works: def printValues(genericInputThatHasMap: Seq[Int]) { genericInputThatHasMap.map { value => println(value) } } 

Thanks in advance! Hooray!

+6
source share
3 answers

First for a brief note on map and foreach . If you are only interested in a side effect operation (for example, printing to standard output or a file, etc.) for each item in your collection, use foreach . If you are interested in creating a new collection, converting each element to its old, use map . When you write xs.map(println) , you will actually print all the elements of the collection, but you will also get a (completely useless) collection of units, as well as potentially confusing future readers of your code, including yourself, expect foreach be used in such a situation.

Now about your problem. You are faced with what, in my opinion, is one of the ugly warts of the Scala standard library - the fact that methods called map and foreach (and flatMap ) receive magic treatment at the language level, which has nothing to do with a particular type, which 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 named my foreach method:

 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 printed foreach method, such as Option[Int] or List[Int] , will compile and work as expected.

Structural types become much more messy when you try to work with map or flatMap , although you are unsatisfied with other methods - they impose some ugly overhead due to their use at runtime. In fact, they should be explicitly included in Scala 2.10 to avoid warnings for these reasons.

As senia's answer indicates, the Scalaz library provides a much more consistent approach to the problem by using enter classes , such as Monad . You would not want to use Monad , although in this case: it is a much more powerful abstraction than you need. You should 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 standard, idiomatic, but perhaps ugly, with structural types, or you can use Scalaz to get a cleaner solution, but at the cost of a new dependency.

+7
source

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 :)

+3
source

You can use MA from scalaz :

 import scalaz._ import Scalaz._ def printValues[A, M[_]](ma: MA[M, A])(implicit e: Each[M]) { ma |>| { println _ } } scala> printValues(List(1, 2, 3)) 1 2 3 scala> printValues(Some(1)) 1 
+2
source

All Articles