13

When I try to compile the small example:

trait Foo[A,B] {
  type F[_,_]
  def foo(): F[A,B]
}

class Bar[A,B] extends Foo[A,B] {
  type F[D,E] = Bar[D,E]
  def foo() = this
}

object Helper {
  def callFoo[A,B,FF <: Foo[A,B]]( f: FF ): FF#F[A,B] =
    f.foo()
}

object Run extends App {
  val x = new Bar[Int,Double]
  val y = Helper.callFoo(x)
  println( y.getClass )
}

I get the error:

[error] src/Issue.scala:20: inferred type arguments
[Nothing,Nothing,issue.Bar[Int,Double]] do not conform to method callFoo's type
parameter bounds [A,B,FF <: issue.Foo[A,B]]
[error]       val y = Helper.callFoo(x)

Apparently, the type inference mechanism is not able to infer A and B out of Bar[A,B]. However, it works if I pass all the types by hand:

val y = Helper.callFoo[Int,Double,Bar[Int,Double]](x)

I there a way to avoid passing types explicitly?

Cœur
  • 37,241
  • 25
  • 195
  • 267
paradigmatic
  • 40,153
  • 18
  • 88
  • 147

2 Answers2

11

You'll have to change the signature of callFoo to this:

def callFoo[A, B, FF[A, B] <: Foo[A, B]](f: FF[A, B]): FF[A, B]#F[A, B] =

You have to tell the compiler that FF is actually a parametrized type.

Jean-Philippe Pellet
  • 59,296
  • 21
  • 173
  • 234
  • @Kipton_Barros: Well, I chose Jean-Phillipe answer only because it implies less refactoring with my current code base. – paradigmatic Jul 31 '11 at 20:42
  • @paradigmatic Yes, Jean-Philippe's answer is quite nice. I found it interesting that although the `FF` parameter doesn't have to be higher-kinded, making it so helps inference. I tried Jean-Philippe's technique on a related question, but couldn't get it to work so far: http://stackoverflow.com/questions/6892781/why-doesnt-scala-fully-infer-type-parameters-when-type-parameters-are-nested/6893057#6893057 – Kipton Barros Jul 31 '11 at 23:46
2

Would it work to use type members instead of parameters?

trait Foo {
  type A
  type B
  type F
  def foo(): F
}

class Bar extends Foo {
  type F = Bar
  def foo() = this
}

object Helper {
  def callFoo[FF <: Foo]( f: FF ): FF#F =
    f.foo()
}

object Run extends App {
  val x = new Bar{type A=Int; type B=Double}
  val y = Helper.callFoo(x)
  println( y.getClass )
}

When using type members, it's useful to know that they can be surfaced as type parameters using refinement, as in Miles Sabin's answer to: Why is this cyclic reference with a type projection illegal?

See also this recent question, which seems similar to yours: Scala fails to infer the right type arguments

Community
  • 1
  • 1
Kipton Barros
  • 21,002
  • 4
  • 67
  • 80