I constantly get the above error message when trying to implement a typeclass. I’ve managed to narrow this to a minimal example:
trait MyTypeclass[A,F[_],B] {
def foo[C](a : Test.Aux[A,F,B], c : C ) : Test.Aux[C,F,B]
}
object MyTypeclass {
trait Ops[A,F[_],B] {
def instance : MyTypeclass[A,F,B]
def self : Test.Aux[A,F,B]
def foo[C](c : C) : Test.Aux[C,F,B] = instance.foo[C](self,c)
}
object syntax {
implicit def toAllOps[A,F[_],B](t : Test.Aux[A,F,B])(implicit tc : MyTypeclass[A,F,B]) = new Ops[A,F,B] {
val instance = tc
val self = t
}
}
}
trait Test[A] {
type F[_]
type B
}
object Test {
type Aux[A,F0[_],B0] = Test[A] { type F[a] = F0[a]; type B = B0 }
def apply[A,F0[_],B0] : Aux[A,F0,B0] = new Test[A] { type F[a] = F0[a]; type B = B0 }
implicit def instance[A,F0[_],B] : MyTypeclass[A,F0,B] = new MyTypeclass[A,F0,B] {
/** Does nothing more than change the type argument `A` to the passed value `C` */
def foo[C](a : Test.Aux[A,F0,B], c : C) : Test.Aux[C,F0,B] = Test[C,F0,B]
}
import MyTypeclass.syntax.toAllOps
toAllOps(Test[String,List,Option[Int]]).foo(2.0)
Test[String,List,Option[Int]].foo(2.0)
val t = Test[String,List,Option[Int]]
t.foo(2.0)
}
What’s curious about this is that the everything but the last line compiles (try commenting it out).
Why does splitting the previous statement into a variable assignment break inference?
I suspect that this has something to do with partial unification, and would be grateful if somebody could give me a detailed explanation of what is triggering this. It’s more important for me to understand what the cause is than to get a workaround, as I’m using a lot of type parameters and this keeps on popping up all the time.
The full error message reads (with -Xlog-implicits):
[info] Test.scala:40: toAllOps is not a valid implicit value for Test.<refinement>.type => ?{def foo: ?} because:
[info] inferred kinds of the type arguments (String,Test.t.F[a],Test.t.B) do not conform t
o the expected kinds of the type parameters (type A,type F,type B).
[info] Test.t.F[a]'s type parameters do not match type F's expected parameters:
[info] type F has one type parameter, but type F has one
[info] t.foo(2.0)
[info] ^