Noel
Noel

Reputation: 2091

How to structure a Scala mixin trait to cast a return to an extended type?

I want to mixin a trait so that I can use a method from to return my own trait type. For example,

> trait M { 
    trait foo {def blah = "foo" }
    def name:foo = { new foo { override def blah = "name"}}}
> trait N extends M { 
    trait bar extends foo {}
    override def name:bar = super.name.asInstanceOf[bar]}
> object t extends N { val baz = name }
> t.name
  java.lang.ClassCastException: M$$anon$1 cannot be cast to N$bar
at N$class.name(<console>:7)
at t$.name(<console>:8)
at t$.<init>(<console>:8)
at t$.<clinit>(<console>)
at .<init>(<console>:10)
at .<clinit>(<console>)
at RequestResult$.<init>(<console>:9)
at RequestResult$.<clinit>(<console>)
at RequestResult$scala_repl_result(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at scala.tools.nsc.Interpreter$Request$$anonfun$loadAndRun$1$$anonfun$apply$17.apply(Interpreter.scala:988)
at scala.tools.nsc.Interpreter$Request$$anonfun$l...

I know that I'm thinking about this in too much an OO fashion by using the asInstanceOf, and am ignorant in regards to something basic about the way traits work in Scala. How should I change N and its sub-types?

Upvotes: 0

Views: 301

Answers (1)

dhg
dhg

Reputation: 52691

M.name constructs and returns a foo. In N.name you call super.name. Since super.name refers to M.name, super.name also constructs and returns a foo. You then take that foo and call .asInstanceOf[bar]. But this doesn't make sense since nothing in your code ever constructed a bar, and, while a bar is a foo, a foo is not necessarily a bar.

If you really want N.name to return a bar, then you need to override M.name so that you explicitly construct a bar and return it.

trait N extends M {
  trait bar extends foo {}
  override def name: bar ={
    val f: foo = super.name                // not what we want; it's not a `bar`
    new bar { override def blah = f.blah } // this is actually a `bar`
  }
}

Now we get:

scala> println(t.name.blah)
name

Upvotes: 2

Related Questions