Below I have a simplified version of a library I'm working on. I modeled this after the scala collection library's CanBuildFrom trait, since my requirements are similar. However, I can't quite get it to infer the correct builder to use in all cases. In the example below, all the methods work if I explicitly specify the builder to use, but I get a compile error for "ambiguous implicit values" when I don't specify the builder, even though it seems that the orderedMultiSignalBuilder
should be strictly more specific than the multiSignalBuilder
. What have I done wrong?
trait Builder[-F, -SourceElement[X] <: Element[X]] {
type Result[X] <: MultiSignal[X]
type TargetElement[X] >: SourceElement[X]
def mapElement[T, U](element : SourceElement[T], value : U) : TargetElement[U] = {
throw new Exception() //not implemented yet
}
def buildNew[T, U](element : TargetElement[T]) : Result[U] = {
throw new Exception() //not implemented yet
}
}
object Builder {
implicit object multiSignalBuilder extends Builder[MultiSignal[Any], Element] {
type Result[X] = MultiSignal[X]
}
implicit def orderedMultiSignalBuilder[P] = new Builder[OrderedMultiSignal[Any] { type Position = P }, ({type λ[α] = OrderedElement[α, P]})#λ]() {
type Result[X] = OrderedMultiSignal[X] { type Position = P }
}
}
trait Element[+T] {
val value : T
}
trait OrderedElement[+T, +P] extends Element[T] {
}
trait MultiSignal[+T] {
type ElementType[+X] <: Element[X]
val element : ElementType[T]
def map[U](f : T => U) (implicit builder : Builder[this.type, ElementType]) : builder.Result[U] =
builder.buildNew(builder.mapElement(element, f(element.value)))
}
trait OrderedMultiSignal[+T] extends MultiSignal[T] {
type Position
type ElementType[+X] = OrderedElement[X, Position]
}
object multiSignal extends MultiSignal[Int] {
type ElementType[+X] = Element[X]
val element = new Element[Int] { val value = 0 }
}
object orderedMultiSignal extends OrderedMultiSignal[Int] {
type Position = Int
val element = new OrderedElement[Int, Int] { val value = 0 }
}
object Test {
multiSignal.map(_.toString)
orderedMultiSignal.map(_.toString) (Builder.multiSignalBuilder)
val result : OrderedMultiSignal[String] { type Position = Int } = orderedMultiSignal.map(_.toString) (Builder.orderedMultiSignalBuilder[Int])
//Next line gets compile error: ambiguous implicit values error
val result2 : OrderedMultiSignal[String] { type Position = Int } = orderedMultiSignal.map(_.toString)
}