3

Let's say I have a type called LongArrayWritable, that is a boxed representation of an Array of Longs. I have implicit definitions that convert between these types:

implicit def boxLongArray(array: Array[Long]) : LongArrayWritable { /*elided*/}
implicit def unboxLongArray(array: LongArrayWritable) : Array[Long] { /*elided*/}

Now, I also have implicits that convert between java.lang.Iterable and scala.collection.List[X] in their generic form:

implicit def iterator2list[X](it : java.lang.Iterable[X]) : List[X] { /* elided */ }
implicit def list2iterator[X](list : List[X]) : java.lang.Iterable[X] { /* elided */ }

With these definitions, can the scala compiler infer an implicit conversion between java.lang.Iterable[LongArrayWritable] and List[Array[Long]] (the equivalent of iterator2list(iterator).map(unboxLongArray(_))), or is this beyond the capabilities of implicits, and therefore requires it's own (explicit?) implicit definition?

Thanks,

Tim

mistertim
  • 5,123
  • 4
  • 20
  • 27
  • Just a stylistic comment; I tend to shy away from implicit conversions of this sort because I think that they hinder readability and obscure potentially costly operations. An example of what I believe is a preferable style can be found here: http://www.scala-lang.org/api/current/scala/collection/JavaConverters$.html (compare to JavaConversions in the same package) – Kris Nuttycombe Mar 30 '11 at 20:28
  • Also, I'm not sure what higher-kinded types have to do with this question; you're not abstracting over type constructors anywhere in this code. – Kris Nuttycombe Mar 30 '11 at 20:30
  • Aah - both good points. I can appreciate how implicits can obscure the cost of a conversion operation - I had been attempting to remove all explicit casts from my code which may not have been a particularly good design decision. Regarding higher-kinded types, I think I may have muddled my terminology slightly here - I may well be confusing higher-kinded types with parameterised types. In fact, I'm not entirely clear on the distinction, but this is probably a seperate question entirely! :-) – mistertim Mar 31 '11 at 08:14
  • 1
    In short, a higher-kinded type allows you to abstract over parameterized types. For example, `List[Int]` is a type; `List` is a type constructor of one argument; it is not a type itself. `List[Int]` is said to have kind `*`, while `List` has kind `* -> *` meaning that it requires another type to give a type. Now, imagine a trait `Foo[M[_]]` - here, you can parameterize Foo with any type constructor of one argument (kind `* -> *`), so for example you could create a `Foo[List]` or `Foo[Option]`, but not a `Foo[List[Int]]` since `List[Int]` has the wrong kind. Hope that helps! – Kris Nuttycombe Apr 01 '11 at 14:48

1 Answers1

7

There is a post that covers this question: How can I chain implicits in Scala? . Essentially, you need to have a view bound for the conversion to LongArrayWritable. That means, the implicit def that converts to LongArrayWritable receives an implicit argument (called view bound) so that the argument to this def is not directly an Array but some type that can be converted to an Array:

object LongArrayWritable {
  implicit def fromArraySource[A <% Array[Long]](a: A): LongArrayWritable = apply(a)
}
case class LongArrayWritable(a: Array[Long])

def test(a: LongArrayWritable): Unit = println("OK")

now this works for Arrays:

test(Array( 1L, 2L, 3L))

however, since Array is not an Iterable and there are no default conversions from Iterable to Array in scope, you need to add one:

implicit def iterable2Array[A: ClassManifest](i: Iterable[A]): Array[A] = i.toArray

then it works:

test(List(1L, 2L, 3L))

the view bound A <% Array[Long] is a shortcut for an implicit argument of type A => Array[Long], so you could have also written

implicit def fromArraySource[A](a: A)(implicit view: A => Array[Long]) ...
Community
  • 1
  • 1
0__
  • 66,707
  • 21
  • 171
  • 266