Search code examples
scalagenericsmixins

How can mixin know type parameters of generic superclass


Let's assume I have a generic type like this one:

class GenericEchoer[T <: Any] {
    var content: T = _
    def echo: String = "Echo: " + content.toString
}

Then it is possible to create a mixin that would allow to extend functionality of GenericEchoer[T] like this:

trait Substitution[T <: AnyRef] extends GenericEchoer[T] {
    def substitute(newValue: T) = { content = newValue }
}

Having those defined, I can instantiate type in this way:

val echoer = new GenericEchoer[Int] with Substitution[Int]

My question is: how to implement similar functionality so that I can omit type parameters in the mixin? In other words, I'd like to be able to instantiate the same type with the following line:

val echoer = new GenericEchoer[Int] with Substitution

This, however, does not work, as Substitution "doesn't know" the underlying type parameter.


Solution

  • You code is wrong, it won't even compile.

    Your GenericEchoer cannot be a class, cause your content member is abstract, or you should init this with a default value:

    class GenericEchoer[T <: AnyRef] {
        var content: T = _
        def echo: String = "Echo: " + T.toString
    }
    

    You can't write T.toString, i guess you wanted content.toString. You can't pass Int to it, cause Int has AnyVal as its supertype, and your upper bound of T is AnyRef.

    self.content in Substitution is also illegal, you should:

    1) make self as a selftype:

    trait Substitution[T <: AnyRef] extends GenericEchoer[T] { self =>
        def substitute(newValue: T) = { self.content = newValue }
    }
    

    2) Replace it with this 3) Just leave { content = newValue }

    As for your problem. No it's not possible. I can suggest you replace class with a trait and type constructor with an abstract type member:

    trait GenericEchoer {
      type T <: AnyRef  
      var content: T = _
      def echo: String = "Echo: " + content.toString
    }
    
    trait Substitution extends GenericEchoer {
      def substitute(newValue: T) { content = newValue }
    }
    
    val enchoer = new GenericEchoer with Substitution { type T = String }
    

    or better

    val enchoer = new GenericEchoer with Substitution { 
      type T = String 
      var content = "Hello" // either case it will be null
    }