Dynamic trait and casting to a type parameter

680 views
Skip to first unread message

Dinko Srkoc

unread,
Nov 13, 2012, 5:43:33 AM11/13/12
to scala...@googlegroups.com
Hi all,

can someone please explain the following behaviour:

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

import language.dynamics

class C(v: Any) extends Dynamic {
def selectDynamic[T](n: String): Option[T] = Option(v.asInstanceOf[T])
def applyDynamic[T](n: String)(): Option[T] = Option(v.asInstanceOf[T])
}

// Exiting paste mode, now interpreting.

import language.dynamics
defined class C

scala> new C(42).foo[Int]().get
res4: Int = 42

scala> new C(42).foo[Int].get
java.lang.ClassCastException: java.lang.Integer cannot be cast to
scala.runtime.Nothing$
...

however, this works:

scala> val i = new C(42).foo[Int]
i: Option[Int] = Some(42)

scala> i.get
res6: Int = 42

So, why `foo[Int].get` causes casting to Nothing?

While at it, here is another question: is it possible to achieve the
same thing, that is the return type being related to given type
parameter, without using `asInstanceOf`?

Thanks,
Dinko

Dinko Srkoc

unread,
Nov 13, 2012, 5:52:29 AM11/13/12
to Naftoli Gugenheim, scala...@googlegroups.com
On 13 November 2012 11:47, Naftoli Gugenheim <nafto...@gmail.com> wrote:
>
> On Tue, Nov 13, 2012 at 5:43 AM, Dinko Srkoc <dinko...@gmail.com> wrote:
>>
>>
>> however, this works:
>>
>> scala> val i = new C(42).foo[Int]
>> i: Option[Int] = Some(42)
>
>
> Does this?
>
> val i = new C(42).foo[Int]()
>

yes, it does. But that doesn't seem surprising to me.

Jan Niehusmann

unread,
Nov 13, 2012, 4:48:26 PM11/13/12
to scala...@googlegroups.com
On 11/13/12 11:43, Dinko Srkoc wrote:
> import language.dynamics
>
> class C(v: Any) extends Dynamic {
> def selectDynamic[T](n: String): Option[T] = Option(v.asInstanceOf[T])
> def applyDynamic[T](n: String)(): Option[T] = Option(v.asInstanceOf[T])
> }
[...]
> scala> new C(42).foo[Int].get
> java.lang.ClassCastException: java.lang.Integer cannot be cast to
> scala.runtime.Nothing$

Looks like a bug to me.

mkInvoke in Typers.scala just ignores the type argument. So, T is
inferred from the outside, and without any other constraints is inferred
as Nothing.

So this works:

scala> new C(42).foo[Int].get : Int
res5: Int = 42

This happens because mkInvoke is called with the following cxTree:
new C.<init>(42).foo[Int].get

Note that the outer element of that tree is a Select(..., "get"), and
that's not handled by the following code:

val (outer, explicitTargs) = cxTree1 match {
case TypeApply(fun, targs) => (fun, targs)
case Apply(TypeApply(fun, targs), args) => (Apply(fun, args), targs)
case t => (t, Nil)
}

That can be fixed by adding another case:

val (outer, explicitTargs) = cxTree1 match {
case TypeApply(fun, targs) => (fun, targs)
case Apply(TypeApply(fun, targs), args) => (Apply(fun, args), targs)
case Select(TypeApply(fun, targs), nme) => (Select(fun, nme), targs)
case t => (t, Nil)
}

With that change, the examples work as expected:

scala> new C(42).foo[Int]().get
res0: Int = 42

scala> new C(42).foo[Int].get
res1: Int = 42



Dinko Srkoc

unread,
Nov 13, 2012, 6:08:02 PM11/13/12
to Jan Niehusmann, scala...@googlegroups.com
On 13 November 2012 22:48, Jan Niehusmann <j...@gondor.com> wrote:
> On 11/13/12 11:43, Dinko Srkoc wrote:
>>
>> import language.dynamics
>>
>> class C(v: Any) extends Dynamic {
>> def selectDynamic[T](n: String): Option[T] = Option(v.asInstanceOf[T])
>> def applyDynamic[T](n: String)(): Option[T] = Option(v.asInstanceOf[T])
>> }
>
> [...]
>
>> scala> new C(42).foo[Int].get
>> java.lang.ClassCastException: java.lang.Integer cannot be cast to
>> scala.runtime.Nothing$
>
>
> Looks like a bug to me.

Paul Phillips offered the following url:
https://issues.scala-lang.org/browse/SI-6551

>
> mkInvoke in Typers.scala just ignores the type argument. So, T is inferred
> from the outside, and without any other constraints is inferred as Nothing.
>
> So this works:
>
> scala> new C(42).foo[Int].get : Int
> res5: Int = 42
>
> This happens because mkInvoke is called with the following cxTree:
> new C.<init>(42).foo[Int].get
>
> Note that the outer element of that tree is a Select(..., "get"), and that's
> not handled by the following code:
>
> val (outer, explicitTargs) = cxTree1 match {
> case TypeApply(fun, targs) => (fun, targs)
> case Apply(TypeApply(fun, targs), args) => (Apply(fun, args), targs)
> case t => (t, Nil)
> }
>
> That can be fixed by adding another case:
>
> val (outer, explicitTargs) = cxTree1 match {
> case TypeApply(fun, targs) => (fun, targs)
> case Apply(TypeApply(fun, targs), args) => (Apply(fun, args), targs)
> case Select(TypeApply(fun, targs), nme) => (Select(fun, nme), targs)
> case t => (t, Nil)
> }
>
> With that change, the examples work as expected:
>
>
> scala> new C(42).foo[Int]().get
> res0: Int = 42
>
>
> scala> new C(42).foo[Int].get
> res1: Int = 42
>

Good find! Thanks for the explanation.

Cheers,
Dinko
Reply all
Reply to author
Forward
0 new messages