Structural subtyping reflection
Asked Answered
W

1

8

Can we get the type of val s: String using reflection from the outside of the function f?

val f = (r: {val s: String}) => {
}
Watchband answered 1/11, 2012 at 21:1 Comment(6)
Under 2.9 or 2.10 or does it not matter?Romeyn
It does not matter 2.9 or 2.10. But I'm usually using 2.9.2.Watchband
I highly doubt it's possible in 2.9 without working with compiler. In 2.10 there is the reflection api that probably will allows inspection of the type of r.Template
Using this info I've tried to reflect on the anonymous function with 2.10.0-RC1 as follows, but stuck on it erasing the type of anonymous function parameters - it could be a bug, I'll report it: import reflect.runtime._, universe._; currentMirror.reflectClass(currentMirror.reflect(f).symbol).symbol.typeSignature.member(newTermName("apply")).asTerm.alternatives.map(_.typeSignature)Icky
That's not a bug. currentMirror.reflect(f) reflects upon a runtime value, therefore it only has access to runtime type information, erased during compilation.Lorenzetti
If you force generation of compile-time type info with type tags, then everything's going to be fine. See my answer below.Lorenzetti
L
10
scala> import scala.reflect.runtime.{universe => ru}
import scala.reflect.runtime.{universe=>ru}

scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._

scala> def typeOf[T: ru.TypeTag](x: T) = ru.typeOf[T] // capture compile-time type info
typeOf: [T](x: T)(implicit evidence$1: reflect.runtime.universe.TypeTag[T])reflect.runtime.universe.Type

scala> val f = (r: {val s: String}) => {}
f: AnyRef{val s: String} => Unit = <function1>

scala> val tpe = typeOf(f)
tpe: reflect.runtime.universe.Type = scala.AnyRef{val s: String} => Unit

scala> ru.showRaw(tpe)
res0: String = TypeRef(ThisType(scala), scala.Function1, List(RefinedType(List(TypeRef(ThisType(scala), newTypeName("AnyRef"), List())), Scope(newTermName("s"))), TypeRef(ThisType(scala), scala.Unit, List())))

scala> val ru.TypeRef(_, _, refinement :: _) = tpe
refinement: reflect.runtime.universe.Type = scala.AnyRef{val s: String}

With Scala reflection one can also generate mocks for structural types as follows: https://gist.github.com/4008389. The linked gist does this using toolboxes and runtime reflection, but this scenario looks implementable with macros as well.

Lorenzetti answered 2/11, 2012 at 2:36 Comment(6)
Which version of Scala do you use? I get a warning on the last statement: warning: abstract type pattern reflect.runtime.universe.TypeRef is unchecked since it is eliminated by erasureGompers
The import is necessary. It brings a class tag for TypeRef into scope, which eliminates the unchecked warning.Lorenzetti
Is there any way to create instance of refinement?Watchband
@Watchband I don't think so. Refinement types are only used during typecheck, no classes are generated for them. So to begin with we have nothing to instantiate.Lorenzetti
@sschaef This happens because TypeRef is an abstract type (scala.reflect.api.Types#TypeRef) and abstract types are erased during compilation, losing their identity. Therefore when the pattern matcher tries to emit something.isInstanceOf[TypeRef], this causes an unchecked warning. However when there's an implicit classtag in scope, the pattern matcher can make use of it to emit something like classtag.runtimeClass.isAssignableFrom(something.getClass) (where classtag points to the underlying scala.reflect.internal.Types#TypeRef, which is a full-fledged class, not an abstract type).Lorenzetti
@Watchband If you're really determined, you can use runtime compilation (which we introduced along with Scala reflection) to generate the necessary classes. If that's what you want, please, ask me for details.Lorenzetti

© 2022 - 2024 — McMap. All rights reserved.