3

I'm trying to enrich Scala collections with my own map method, and I'm close but the implicit conversion doesn't work. Besides that, is there anything else I'm missing here? I'm looking at various other resources on the Web, including SO answers that this question is being marked as duplicating, and many are missing something here and there (e.g. using C[A] <: GenTraversable[A], using b() instead of b(xs), forgetting about Array, forgetting about BitSet, etc.).

implicit def conv[A,C](xs: C)(implicit ev: C <:< GenTraversableLike[A,C]) = new {
  def mymap[B,D](f: A => B)(implicit b: CanBuildFrom[C,B,D]): D = b(xs).result // placeholder
}

scala> conv(List(1,2,3))
res39: java.lang.Object{def mymap[B,D](f: Int => B)(implicit b: scala.collection.generic.CanBuildFrom[List[Int],B,D]): D} = $$$$2c9d7a9074166de3bf8b66cf7c45a3ed$$$$anon$1@3ed0eea6

scala> conv(List(1,2,3))mymap(_+1)
res40: List[Int] = List()

scala> conv(BitSet(1,2,3))mymap(_+1)
res41: scala.collection.immutable.BitSet = BitSet()

scala> conv(BitSet(1,2,3))mymap(_.toFloat)
res42: scala.collection.immutable.Set[Float] = Set()

scala> List(1,2,3)mymap(_+1)
<console>:168: error: Cannot prove that List[Int] <:< scala.collection.IterableLike[A,List[Int]].
              List(1,2,3)mymap(_+1)
                  ^

scala> implicit def conv[A, C](xs: C)(implicit ev: C => GenTraversable[A]) = new {
     | def mymap[B,D](f: A => B)(implicit b: CanBuildFrom[GenTraversable[A],B,D]): D =
     | xs map f
     | }
conv: [A, C](xs: C)(implicit ev: C => scala.collection.GenTraversable[A])java.lang.Object{def mymap[B,D](f: A => B)(implicit b: scala.collection.generic.CanBuildFrom[scala.collection.GenTraversable[A],B,D]): D}

scala> conv(Array(1)) mymap (_+1)
res6: scala.collection.GenTraversable[Int] = ArrayBuffer(2)

scala> Array(1) mymap (_+1)
<console>:68: error: No implicit view available from Array[Int] => scala.collection.GenTraversable[A].
              Array(1) mymap (_+1)
                   ^
Yang
  • 16,037
  • 15
  • 100
  • 142
  • possible duplicate of [Writing my own generic map functioni](http://stackoverflow.com/questions/6798260/writing-my-own-generic-map-functioni) – Daniel C. Sobral Dec 12 '11 at 13:30
  • You're adding requirements that you don't need, at least not in your example. If you come up with a use case where you must have `<:<` instead of something else, fine, but show us that use case in the example. – Rex Kerr Dec 12 '11 at 17:15
  • @RexKerr I was using `<:<` because I couldn't get `<:` to work - see http://stackoverflow.com/questions/8472206/why-do-i-need-an-explicit-evidence-type-why-does-this-scala-type-bound-fail/8473044#8473044 – Yang Dec 12 '11 at 20:57

2 Answers2

4

I've answered this very question about type inference just last week. Here's the code:

implicit def conv[A,C <: GenTraversable[A]](xs: C with GenTraversableLike[A,C]) = new {
  def mymap[B,D](f: A => B)(implicit b: CanBuildFrom[C,B,D]): D = {
    val builder = b(xs)
    xs foreach { x => builder += f(x) }
    builder.result
  }
}

I could have used GenTraversable instead of GenTraversableLike in this particular case. I prefer the later because it offers more.

The problem is that declaring [A, C <: GenTraversable[A]] does not instruct Scala to infer the type of A from the type of C. Types are inferred based on how they are used in the parameters, and then checked against the boundaries specified by the type parameters.

So when I write xs: C with GenTraversable[A], I let Scala know it should infer A from xs. And writing GenTraversableLike[A, C] tells Scala it should pick a collection that returns C for methods that return the same collection. This means you can call filter and get C back, instead of getting GenTraversable back.

As for wishing to include views, that I don't know how you could accomplish.

Community
  • 1
  • 1
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • Ugh, yet another rule to remember: use `with` to get around http://stackoverflow.com/questions/8472206. Could you explain why you constrain C to be both `GenTraversable[A]` and `GenTraversableLike[A,C]`? Why/when does `with` work but not `<:<` (and why not always use `with` instead of `<:`)? (A related question I have is http://stackoverflow.com/questions/8472134/why-does-this-scala-implicit-conversion-fail-when-explicit-conversion-works)? I'll mark your question as accepted but any explanation of the nuances here would be much appreciated. – Yang Dec 12 '11 at 21:18
  • Argh, your solution is no go: it doesn't work with `Array`s, since they in turn need an implicit conversion to become a collection, so `with` doesn't work here. In general this needs to handle any type that can be viewed as a collection, because I want things to behave as if I added a method to the collection hierarchy itself. Updated my question. Also, these are the types of issues I was hoping to catch by asking this question. – Yang Dec 12 '11 at 21:47
1

I have answered a similar question here. You can also refer to this thread where Rex Kerr explains how to perform such pimping in general.

Community
  • 1
  • 1
missingfaktor
  • 90,905
  • 62
  • 285
  • 365
  • @DanielC.Sobral: That one isn't pimpy. :) – missingfaktor Dec 12 '11 at 13:54
  • I saw both threads already, and they don't answer my question. For instance, the type parameters I use here seem to match up with those in @missingfactor's other answer, but the implicit fails to work - feel free to tell me how to get it working. RexKerr's answer relies on C and D being type constructors (and while I can make C work as a type constructor, that's again broken, and D I can't make work at all as a type constructor). – Yang Dec 12 '11 at 14:32