After working through some examples of Scala higher-kinded types in this tutorial, I started playing around with (a) implicits and (b) techniques for writing methods that generically handle subclasses of a trait that is defined as a higher-kinded type. I already got a great answer on (b) here which is moving me closer to solving the problem below:
I want to create a factory for creating singleton containers (Sets, Lists, Arrays) that wrap the item that I pass in to the factory function. [ Even if there is already a Scala library that does this, I'm doing this as a learning exercise. But, hey... if there is one, please let me know!]
The end result should let me do this:
scala> var j: Set[String] = wrapItemInContainer("foo")
j: Set[String] = Set(foo)
scala> var k: List[Int] = wrapItemInContainer(9)
k: List[Int] = List(9)
The solution I came up with is shown below. I have to supply an annoying dummy final argument so that I can help the compiler figure out what kind of container I want. This works, but I am puzzled by the fact that the type signature of the variable that I want to assign the result to (j, k..etc.) is not giving the compiler enough information to figure out which implicitly defined ContainerFactory needs to be used.
This clunky solution works:
trait ContainerFactory[M[_]] { def put[A](x: A): M[A] }
implicit val factory = new ContainerFactory[List] { def put[A](x: A) = List(x) } // factory for List containers
implicit val factory2 = new ContainerFactory[Set] { def put[A](x: A) = Set(x)} // factory for Set containers
def wrapItemInContainer[ M[A]: ContainerFactory, A](item: A, helper: M[A]) : M[A] = {
val c = implicitly[ContainerFactory[M]]
c.put(item)
}
var j: List[Int] = wrapItemInContainer(9, List(0))
But I really want something with out the noisy second argument:
def wrapItemInContainer[ M[A]: ContainerFactory, A](item: A) : M[A] = {
val c = implicitly[ContainerFactory[M[A]]]
c.put(item)
}
var j: List[Int] = wrapItemInContainer(9) // this does not work.
I get this error:
<console>:17: error: ambiguous implicit values:
both value factory of type => ContainerFactory[List]
and value factory2 of type => ContainerFactory[Set]
match expected type ContainerFactory[M]
var j: List[Int] = wrapItemInContainer(9)
Any ideas or tips are much appreciated !
-chris
You need to make the factory covariant in M. See https://groups.google.com/forum/#!topic/scala-language/dQEomVCH3CI and https://issues.scala-lang.org/browse/SI-7332.
This compiles:
import language.higherKinds
trait Factory[+M[_], A] { def put(x: A): M[A] }
implicit def factory1[A] =
new Factory[List, A] { def put(x: A) = List(x) }
implicit def factory2[A] =
new Factory[Set, A] { def put(x: A) = Set(x) }
def wrap[M[_], A](a: A)(
implicit factory: Factory[M, A]): M[A] =
factory.put(a)
val j: Set[String] = wrap("foo")
val k: List[Int] = wrap(9)
I've taken the liberty of shortening your names. I also followed the advice you received at how to generically handle Scala Higher-kinded types when coding factories for generating containers of items of type 'X' to make A a type parameter on the factory itself.
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