1

Scala type inference system has its limitations.

For example in the following classes. I'd like to have a factory FooFactory that creates instances of Foo and a FooChanger that can modify instances created by the FooFactory.

After I create my Foo and my FooChanger with the factory, I'd like to use the changer in objects created by the FooFactory object, however, scala is not able to recognize that the type myOwnChanger.fooFactory.Foo and factory.Foo are the same.

A possible solution is to cast, or also, to use only factory.theFooChanger.fooFactory in all client code. Both are ugly solutions and force the client to do something unnatural.

The question is: What can be done to avoid the problem for the client? Is there a way to tell the inferencer that for the rest of the code myOwnChanger.fooFactory.Foo and factory.Foo are the same?

class FooChangerFactory(val fooFactory: FooFactory) {
class FooChanger
  def change(foo: fooFactory.Foo) = foo
}

class FooFactory(i: Int) {
  class Foo private[FooFactory] ()

  def create = new Foo
  def theFooChanger = new FooChangerFactory(this)
}

val factory = new FooFactory(1)
val myFoo = factory.create
val myOwnChanger = factory.theFooChanger
myOwnChanger.change(myFoo) // error here! it expects myOwnChanger.fooFactory.Foo
mundacho
  • 229
  • 1
  • 8

1 Answers1

3

Replace

def change(foo: fooFactory.Foo) = foo

with

def change(foo: FooFactory#Foo) = foo

The problem exists because, in Scala, inner types are path-dependent (i.e. for each FooFactory instance, its inner type Foo is "unique" to that instance). The usage of # is called type projection. The way to think of this is that FooFactory#Foo is the common supertype of all the path-dependent Foo in all instances of FooFactory.

Community
  • 1
  • 1
mikołak
  • 9,605
  • 1
  • 48
  • 70