Cryptic "type F has one type parameter, but type F has one"


#1

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]   ^

#2

And I’ve discovered that if I’m move the F[_] out of the refinement into a type parameter instead, then everything appears to compile. i.e.:

trait Test[A,F[_]] {
  type B
}

I’ve no idea why though. Is the syntax for type constructor

type F[a] = F0[a]

in the o.p. not correct somehow?


#3

I suspect it’s due to the stability of the path t, because if you make it def or var your example works.

For sanity’s sake, I hope you aren’t generally writing methods like toAllOps like that (lifting type members to method type parameters) instead of with dependent method types.