overridable default type parameter in Scala

Viewed 69

I am trying to expose a class parametrized with a type alias:

trait Tr[T] {
  var x: T = _
}

abstract class Foo {
  type MyParameter

  class SomeClass extends SomeOtherClass with Tr[MyParameter]
}

class Bar extends Foo {
  override type MyParameter = Int

  val myObj = new SomeClass
  myObj.x = 6
}

class Qux extends Foo {
  override type MyParameter = String

  val myObj = new SomeClass
  myObj.x = "hello" 
}

This works fine.

Now, what I would like to do is to specify a default value for the MyParameter type. Like this:

abstract class Foo {
  type MyParameter = String
  // ...
}

// ...

class Qux extends Foo {
  val myObj = new SomeClass
  myObj.x = "hello"
}

If I do this, however, class Bar will fail with a type error -- apparently, at that point, SomeClass is already fixed to String.

What can I do to fix this? Alternately, what other approach can I take to have SomeClass parametrized by a type that has a default, but can be overriden in subclasses of Foo?

Note that SomeClass will be more complex than shown, so I don't want the users to have to override the definition of SomeClass. Also, all uses of X.SomeClass should use the same Tr[T], so I don't want to make SomeClass itself parametrizable.

1 Answers

If you omit the assignment you can get to the root cause of the issue:

scala> :paste
// Entering paste mode (ctrl-D to finish)

trait T[T] {
  var x: T = _
}

abstract class Foo {
  type Param = String

  class Child extends T[Param]
}

class Bar extends Foo {
  (new Child).x = "foo"
}

class Qux extends Foo {
  override type Param = Int
}

// Exiting paste mode, now interpreting.

<pastie>:26: error: overriding type Param in class Foo, which equals String;
 type Param has incompatible type
  override type Param = Int

Overriding a type follows the same rules as overriding a def, val or var: the overriding type has to be compatible with the type it is overriding.

The same way you can't override toString to return an Int, for example.

scala> :paste
// Entering paste mode (ctrl-D to finish)

class Test {
  override def toString: Int = 1
}

// Exiting paste mode, now interpreting.

<pastie>:12: error: overriding method toString in class Object of type ()String;
 method toString has incompatible type
  override def toString: Int = 1
               ^

My suggestion would be to split the hierarchy, so when you need a Foo that has a Tr[String] into a FooString, and if you need other type you can extend Foo.

Related