Constructing an object based on passed parameter. But the parameter is not String.
I found the solution how to do it with String
Scala instantiate objects from String classname
but I believe that it can be done nicer.
Let's say the following classes:
sealed trait Figure
object Figure {
final case class Circle(radius: Double) extends Figure
final case class Square(a: Double) extends Figure
}
And let's define a function (which doesn't make sense) which take a parameter based on I can invoke the proper constructor:
val construct123: Figure => Either[String, Figure] = (figure: Figure) => Right(figure.apply(1.23))
I want to invoke
construct123(Circle)
//or
construct123(Square)
Is it even possible?
The easiest would be to modify the signature of construct123 slightly
def construct123(figure: Double => Figure): Either[String, Figure] =
Right(figure(1.23))
construct123(Circle.apply) // Right(Circle(1.23))
construct123(Square.apply) // Right(Square(1.23))
construct123(Circle(_)) // Right(Circle(1.23))
construct123(Square(_)) // Right(Square(1.23))
construct123(Circle) // Right(Circle(1.23))
construct123(Square) // Right(Square(1.23))
Or construct123 can be written as a higher-order function
val construct123: (Double => Figure) => Either[String, Figure] =
figure => Right(figure(1.23))
Difference between method and function in Scala
Circle and Square in construct123(Circle) and construct123(Square) are not the case classes Circle and Square but their companion objects
Class companion object vs. case class itself
So actually you want to transform an object into an instance of its companion class. You can do this for example with a macro
// libraryDependencies += scalaOrganization.value % "scala-reflect" % scalaVersion.value
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def construct123[A](figure: A): Either[String, Figure] = macro construct123Impl[A]
def construct123Impl[A: c.WeakTypeTag](c: blackbox.Context)(figure: c.Tree): c.Tree = {
import c.universe._
val companionClass = weakTypeOf[A].companion
q"_root_.scala.Right.apply(new $companionClass(1.23))" // using constructor of the case class
}
or
def construct123Impl[A: c.WeakTypeTag](c: blackbox.Context)(figure: c.Tree): c.Tree = {
import c.universe._
val A = symbolOf[A].asClass.module
q"_root_.scala.Right.apply($A.apply(1.23))" // using apply method of the companion object
}
Testing (in a different subproject):
construct123(Circle) // Right(Circle(1.23))
construct123(Square) // Right(Square(1.23))
// scalacOptions += "-Ymacro-debug-lite"
//scalac: scala.Right.apply(new Macros.Figure.Circle(1.23))
//scalac: scala.Right.apply(new Macros.Figure.Square(1.23))
//scalac: scala.Right.apply(Circle.apply(1.23))
//scalac: scala.Right.apply(Square.apply(1.23))
You can hide the work with macros in type classes (defined with whitebox implicit macros). The type class ToCompanion below is similar to HasCompanion in Get companion object of class by given generic type Scala (answer). The type class Generic from Shapeless (used below to define construct123) is also macro-generated. Some intros to type classes (ordinary, not macro-generated): 1 2 3 4 5 6 7 8 9
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
// type class
trait ToCompanion[A] {
type Out
}
object ToCompanion {
type Aux[A, Out0] = ToCompanion[A] {type Out = Out0}
// materializer
def apply[A](implicit tcc: ToCompanion[A]): ToCompanion.Aux[A, tcc.Out] = tcc
// def apply[A](implicit tcc: ToCompanion[A]): tcc.type = tcc
// instance of the type class
implicit def mkToCompanion[A, B]: ToCompanion.Aux[A, B] = macro mkToCompanionImpl[A]
// implicit def mkToCompanion[A]: ToCompanion[A] = macro mkToCompanionImpl[A] // then implicitly[ToCompanion.Aux[Circle, Circle.type]] doesn't compile in spite of white-boxity
def mkToCompanionImpl[A: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
import c.universe._
val A = weakTypeOf[A]
val companion = A.companion
val ToCompanion = weakTypeOf[ToCompanion[A]]
q"new $ToCompanion { type Out = $companion }"
}
}
implicitly[ToCompanion.Aux[Circle, Circle.type]] // compiles
implicitly[ToCompanion.Aux[Circle.type, Circle]] // compiles
val tc = ToCompanion[Circle.type]
implicitly[tc.Out =:= Circle] // compiles
val tc1 = ToCompanion[Circle]
implicitly[tc1.Out =:= Circle.type] // compiles
// libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.10"
import shapeless.{::, Generic, HNil}
def construct123[A, B <: Figure](figure: A)(implicit
toCompanion: ToCompanion.Aux[A, B],
generic: Generic.Aux[B, Double :: HNil]
): Either[String, Figure] = Right(generic.from(1.23 :: HNil))
construct123(Circle) // Right(Circle(1.23))
construct123(Square) // Right(Square(1.23))
Since all the classes are now known at compile time it's better to use compile-time reflection (the above macros). But in principle runtime reflection can be used too
import scala.reflect.runtime.{currentMirror => rm}
import scala.reflect.runtime.universe._
def construct123[A: TypeTag](figure: A): Either[String, Figure] = {
val classSymbol = symbolOf[A].companion.asClass
//val classSymbol = typeOf[A].companion.typeSymbol.asClass
val constructorSymbol = typeOf[A].companion.decl(termNames.CONSTRUCTOR).asMethod
val res = rm.reflectClass(classSymbol).reflectConstructor(constructorSymbol)(1.23).asInstanceOf[Figure]
Right(res)
}
or
import scala.reflect.ClassTag
import scala.reflect.runtime.{currentMirror => rm}
import scala.reflect.runtime.universe._
def construct123[A: TypeTag : ClassTag](figure: A): Either[String, Figure] = {
val methodSymbol = typeOf[A].decl(TermName("apply")).asMethod
val res = rm.reflect(figure).reflectMethod(methodSymbol).apply(1.23).asInstanceOf[Figure]
Right(res)
}
Or you can use structural types aka duck typing (i.e. also runtime reflection under the hood)
import scala.language.reflectiveCalls
def construct123(figure: { def apply(x: Double): Figure }): Either[String, Figure] =
Right(figure(1.23))
Related
I'm trying to solve a problem that may not be possible in Scala.
I want to have a Trait to solve default constructors
trait Builder[T <: Buildable] {
def build(code: String): T = new T(code)
def build: T = new T("bar")
}
So extending the Trait on the companion object automatically has access to functions that creates the class with specific constructors and parameters
class A(code: String) extends Buildable
object A extends Builder[A]
Extending the Trait, the companion object has the constructors
A.build("foo")
A.build
Is this possible in Scala?
Also tried abstract classes, but hadn't had any success
trait Builder[T <: BuildableClass] {
def build(code: String): T = new T(code)
def build: T = new T("bar")
}
abstract class BuildableClass(code: String)
class A(code: String) extends BuildableClass(code)
object A extends Builder[A]
Thanks in advance
Edit: currently locked on Scala 2.12
Because of the type erasure, in ordinary code new T is allowed only for class type T, not an abstract type/type parameter.
In Scala, is it possible to instantiate an object of generic type T?
How to create an instance of type T at runtime with TypeTags
Class type required but T found
An alternative to runtime reflection (see #StanislavKovalenko's answer) is macros. new T is possible there because during macro expansion T is not erased yet.
import scala.language.experimental.macros
import scala.reflect.macros.blackbox // libraryDependencies += scalaOrganization.value % "scala-reflect" % scalaVersion.value
abstract class BuildableClass(code: String)
trait Builder[T <: BuildableClass] {
def build(code: String): T = macro BuilderMacros.buildImpl[T]
def build: T = macro BuilderMacros.buildDefaultImpl[T]
}
class BuilderMacros(val c: blackbox.Context) {
import c.universe._
def buildImpl[T: WeakTypeTag](code: Tree): Tree = q"new ${weakTypeOf[T]}($code)"
def buildDefaultImpl[T: WeakTypeTag]: Tree = q"""new ${weakTypeOf[T]}("bar")"""
}
// in a different subproject
class A(code:String) extends BuildableClass(code)
object A extends Builder[A]
A.build("foo") // scalac: new A("foo")
A.build // scalac: new A("bar")
Alternative implementations:
trait Builder[T <: BuildableClass] {
def build(code: String): T = macro BuilderMacros.buildImpl
def build: T = macro BuilderMacros.buildDefaultImpl
}
class BuilderMacros(val c: blackbox.Context) {
import c.universe._
val tpe = c.prefix.tree.tpe.baseType(symbolOf[Builder[_]]).typeArgs.head
def buildImpl(code: Tree): Tree = q"new $tpe($code)"
def buildDefaultImpl: Tree = q"""new $tpe("bar")"""
}
class BuilderMacros(val c: blackbox.Context) {
import c.universe._
val symb = c.prefix.tree.symbol.companion
def buildImpl(code: Tree): Tree = q"new $symb($code)"
def buildDefaultImpl: Tree = q"""new $symb("bar")"""
}
class BuilderMacros(val c: blackbox.Context) {
import c.universe._
val tpe = c.prefix.tree.tpe.companion
def buildImpl(code: Tree): Tree = q"new $tpe($code)"
def buildDefaultImpl: Tree = q"""new $tpe("bar")"""
}
class BuilderMacros(val c: blackbox.Context) {
import c.universe._
val tpe = symbolOf[Builder[_]].typeParams.head.asType.toType
.asSeenFrom(c.prefix.tree.tpe, symbolOf[Builder[_]])
def buildImpl(code: Tree): Tree = q"new $tpe($code)"
def buildDefaultImpl: Tree = q"""new $tpe("bar")"""
}
One of the potential solution that uses a reflection looks a bit ugly, but it works.
import scala.reflect._
trait Builder[T <: Buildable] {
def build(code: String)(implicit ct: ClassTag[T]): T =
ct.runtimeClass.getConstructors()(0).newInstance(code).asInstanceOf[T]
def build(implicit ct: ClassTag[T]): T =
ct.runtimeClass.getConstructors()(0).newInstance("bar").asInstanceOf[T]
}
trait Buildable
class A(code: String) extends Buildable {
def getCode = code
}
object A extends Builder[A]
val a: A = A.build
println(a.getCode)
The problem is that your Builder trait doesn't know anything about how to construct your instances. You can get this info from runtime with reflection or from compile time with macros.
Found a much cleaner solution to this problem without using macros / reflection / implicits
trait Buildable
trait Builder[T <: Buildable] {
self =>
def apply(code: String): T
def build: T = self.apply("foo")
def build(code: String): T = self.apply(code)
}
case class A(code: String) extends Buildable {
def getCode: String = code
}
object A extends Builder[A]
A.build("bar").getCode
A.build.getCode
In Scala 2.13 I have a case where I define some operation for all of types extending some sealed trait EnumType. I made it working but I'd like getTypeClass function not to be dependant on concrete types extending EnumType. Now I have to visit this function any time EnumType changes and add or remove some pattern. Is there a way to get instances of Operation type class for EnumType types but without pattern matching on all of them?
sealed trait EnumType
case object Add10 extends EnumType
case object Add50 extends EnumType
trait Operation[+T] {
def op(a: Int): Int
}
implicit val add10: Operation[Add10.type] = (a: Int) => a + 10
implicit val add50: Operation[Add50.type] = (a: Int) => a + 50
def getTypeClass(enumType: EnumType): Operation[EnumType] = {
// I need to modify this pattern matching
// every time EnumType changes
enumType match {
case Add10 => implicitly[Operation[Add10.type]]
case Add50 => implicitly[Operation[Add50.type]]
}
// I'd wish it could be done with without pattern matching like (it does not compile):
// implicitly[Operation[concrete_type_of(enumType)]]
}
// value of enumType is dynamic - it can be even decoded from some json document
val enumType: EnumType = Add50
println(getTypeClass(enumType).op(10)) // prints 60
EDIT
That's the way I wish it was called without using explicit subtypes of EnumType (using circe in this example to decode json) :
case class Doc(enumType: EnumType, param: Int)
implicit val docDecoder: Decoder[Doc] = deriveDecoder
implicit val enumTypeDecoder: Decoder[EnumType] = deriveEnumerationDecoder
decode[Doc]("""{"enumType": "Add10", "param": 10}""").map {
doc =>
println(getTypeClass(doc.enumType).call(doc.param))
}
Since you only know that statically enumType has type just EnumType and want to match based on runtime value/runtime class of enumType, you'll have to use some kind of reflection:
either runtime compilation with reflective toolbox (resolving implicits at runtime)
// libraryDependencies += scalaOrganization.value % "scala-compiler" % scalaVersion.value
import scala.reflect.runtime.{currentMirror => cm}
import scala.reflect.runtime.universe._
import scala.tools.reflect.ToolBox
val tb = cm.mkToolBox()
def getTypeClass(enumType: EnumType): Operation[EnumType] =
tb.eval(q"_root_.scala.Predef.implicitly[Operation[${cm.moduleSymbol(enumType.getClass)}]]")
.asInstanceOf[Operation[EnumType]]
or
def getTypeClass(enumType: EnumType): Operation[EnumType] =
tb.eval(tb.untypecheck(tb.inferImplicitValue(
appliedType(
typeOf[Operation[_]].typeConstructor,
cm.moduleSymbol(enumType.getClass).moduleClass.asClass.toType
),
silent = false
))).asInstanceOf[Operation[EnumType]]
or
def getTypeClass(enumType: EnumType): Operation[EnumType] = {
val cases = typeOf[EnumType].typeSymbol.asClass.knownDirectSubclasses.map(subclass => {
val module = subclass.asClass.module
val pattern = pq"`$module`"
cq"$pattern => _root_.scala.Predef.implicitly[Operation[$module.type]]"
})
tb.eval(q"(et: EnumType) => et match { case ..$cases }")
.asInstanceOf[EnumType => Operation[EnumType]]
.apply(enumType)
}
or a macro (automating the pattern matching)
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def getTypeClass(enumType: EnumType): Operation[EnumType] = macro getTypeClassImpl
def getTypeClassImpl(c: blackbox.Context)(enumType: c.Tree): c.Tree = {
import c.universe._
val cases = typeOf[EnumType].typeSymbol.asClass.knownDirectSubclasses.map(subclass => {
val module = subclass.asClass.module
val pattern = pq"`$module`"
cq"$pattern => _root_.scala.Predef.implicitly[Operation[$module.type]]"
})
q"$enumType match { case ..$cases }"
}
//scalac: App.this.enumType match {
// case Add10 => _root_.scala.Predef.implicitly[Macro.Operation[Add10.type]]
// case Add50 => _root_.scala.Predef.implicitly[Macro.Operation[Add50.type]]
//}
Since all the objects are defined at compile time I guess that a macro is better.
Covariant case class mapping to its base class without a type parameter and back
Getting subclasses of a sealed trait
Iteration over a sealed trait in Scala?
You can even make a macro whitebox, then using runtime reflection in the macro you can have Add50 type statically (if the class is known during macro expansion)
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
def getTypeClass(enumType: EnumType): Operation[EnumType] = macro getTypeClassImpl
def getTypeClassImpl(c: whitebox.Context)(enumType: c.Tree): c.Tree = {
import c.universe._
val clazz = c.eval(c.Expr[EnumType](c.untypecheck(enumType))).getClass
val rm = scala.reflect.runtime.currentMirror
val symbol = rm.moduleSymbol(clazz)
//q"_root_.scala.Predef.implicitly[Operation[${symbol.asInstanceOf[ModuleSymbol]}.type]]" // implicit not found
//q"_root_.scala.Predef.implicitly[Operation[${symbol/*.asInstanceOf[ModuleSymbol]*/.moduleClass.asClass.toType.asInstanceOf[Type]}]]" // implicit not found
// "migrating" symbol from runtime universe to macro universe
c.parse(s"_root_.scala.Predef.implicitly[Operation[${symbol.fullName}.type]]")
}
object App {
val enumType: EnumType = Add50
}
val operation = getTypeClass(App.enumType)
operation: Operation[Add50.type] // not just Operation[EnumType]
operation.op(10) // 60
How to accept only a specific subtype of existential type?
In a scala macro, how to get the full name that a class will have at runtime?
def getTypeClass[T <: EnumType : Operation](t: T) = implicitly[Operation[T]]
println(getTypeClass(Add50).op(10))
In fact, you don't even need getTypeClass at all:
def operate[T <: EnumType : Operation](t: T)(param: Int) = implicitly[Operation[T]].op(param)
println(operate(Add50)(10))
The Foo : Bar notation I used above is equivalent to this:
def operate[T <: EnumType](t: T)(param: Int)(op: Operation[T]) = op.op(param)
Note, that you are not actually using instances of Add50 and Add10 anywhere. They could just be traits rather than objects (IMO, that better reflects the intent):
sealed trait EnumType
trait Add10 extends EnumType
trait Add50 extends EnumType
trait Operation[+T] {
def op(a: Int): Int
}
implicit val add10: Operation[Add10] = (a: Int) => a + 10
implicit val add50: Operation[Add50] = (a: Int) => a + 50
def operate[T <: EnumType : Operation](param: Int) =
implicitly[Operation[T]].op(param)
println(operate[Add50](10))
Say I have this hierarchy
trait Base {
val tag: String
}
case class Derived1(tag: String = "Derived 1") extends Base
case class Derived2(tag: String = "Derived 2") extends Base
//etc ...
and I want to define method with following signature
def tag[T <: Base](instance: T, tag: String): T
that returns an instance of type T with modified tag: String. So when e.g. a Derived1 instance is passed in a modified instance of the same type is returned.
This goal could be easily accomplished by using mutable tag variable var tag: String. How to achieve desired behaviour using scala and functional programming?
My thought:
I could create a type class and its instances
trait Tagger[T] {
def tag(t: T, state: String): T
}
implicit object TaggerDerived1 extends Tagger[Derived1] {
override def tag(t: Derived1, state: String): Derived1 = ???
}
implicit object TaggerDerived2 extends Tagger[Derived2] {
override def tag(t: Derived2, state: String): Derived2 = ???
}
implicit object TaggerBase extends Tagger[Base] {
override def tag(t: Base, state: String): Base = ???
}
and a method
def tag[T <: Base](instance: T, tag: String)(implicit tagger: Tagger[T]): T = tagger.tag(instance, tag)
This is not ideal, because first of all user must be aware of this when defining their own derived classes. When not defining one, the implicit resolution would fall back to base implementation and narrow the returning type.
case class Derived3(tag: String = "Derived 3") extends Base
tag(Derived3(), "test") // falls back to `tag[Base](...)`
Now I am leaning towards using mutable state by employing var tag: String. However, I would love to hear some opinions how to resolve this purely functionally in scala.
You can derive your type class Tagger and then the users will not have to define its instances for every new case class extending Base
// libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.10"
import shapeless.labelled.{FieldType, field}
import shapeless.{::, HList, HNil, LabelledGeneric, Witness}
trait Tagger[T] {
def tag(t: T, state: String): T
}
trait LowPriorityTagger {
implicit def notTagFieldTagger[K <: Symbol : Witness.Aux, V, T <: HList](implicit
tagger: Tagger[T]
): Tagger[FieldType[K, V] :: T] =
(t, state) => t.head :: tagger.tag(t.tail, state)
}
object Tagger extends LowPriorityTagger {
implicit def genericTagger[T <: Base with Product, L <: HList](implicit
generic: LabelledGeneric.Aux[T, L],
tagger: Tagger[L]
): Tagger[T] = (t, state) => generic.from(tagger.tag(generic.to(t), state))
implicit val hnilTagger: Tagger[HNil] = (_, _) => HNil
implicit def tagFieldTagger[T <: HList]:
Tagger[FieldType[Witness.`'tag`.T, String] :: T] =
(t, state) => field[Witness.`'tag`.T](state) :: t.tail
}
case class Derived1(tag: String = "Derived 1") extends Base
case class Derived2(tag: String = "Derived 2") extends Base
case class Derived3(i: Int, tag: String = "Derived 3", s: String) extends Base
tag(Derived1("aaa"), "bbb") // Derived1(bbb)
tag(Derived2("ccc"), "ddd") // Derived2(ddd)
tag(Derived3(1, "ccc", "xxx"), "ddd") // Derived3(1,ddd,xxx)
Alternatively for single-parameter case classes you can constrain T so that it has .copy
import scala.language.reflectiveCalls
def tag[T <: Base {def copy(tag: String): T}](instance: T, tag: String): T =
instance.copy(tag = tag)
For multi-parameter case classes it's harder to express in types the existence of .copy because the method signature becomes unknown (to be calculated).
So you can make tag a macro
// libraryDependencies += scalaOrganization.value % "scala-reflect" % scalaVersion.value
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def tag[T <: Base](instance: T, tag: String): T = macro tagImpl
def tagImpl(c: blackbox.Context)(instance: c.Tree, tag: c.Tree): c.Tree = {
import c.universe._
q"$instance.copy(tag = $tag)"
}
Or you can use runtime reflection (Java or Scala, using Product functionality or not)
import scala.reflect.{ClassTag, classTag}
import scala.reflect.runtime.{currentMirror => rm}
import scala.reflect.runtime.universe.{TermName, termNames}
def tag[T <: Base with Product : ClassTag](instance: T, tag: String): T = {
// Product
val values = instance.productElementNames.zip(instance.productIterator)
.map {case fieldName -> fieldValue => if (fieldName == "tag") tag else fieldValue}.toSeq
// Java reflection
// val clazz = instance.getClass
// clazz.getMethods.find(_.getName == "copy").get.invoke(instance, values: _*).asInstanceOf[T]
// clazz.getConstructors.head.newInstance(values: _*).asInstanceOf[T]
// Scala reflection
val clazz = classTag[T].runtimeClass
val classSymbol = rm.classSymbol(clazz)
// val copyMethodSymbol = classSymbol.typeSignature.decl(TermName("copy")).asMethod
// rm.reflect(instance).reflectMethod(copyMethodSymbol)(values: _*).asInstanceOf[T]
val constructorSymbol = classSymbol.typeSignature.decl(termNames.CONSTRUCTOR).asMethod
rm.reflectClass(classSymbol).reflectConstructor(constructorSymbol)(values: _*).asInstanceOf[T]
}
I am trying to use fundep materialization to add compile time safety. The return type of the macro is dependent on the string which is passed into the macro (i.e. the implicit in Foo.scala).
My end goal is a DSL which looks something like this,
"Eggs, Bacon".shoppingList.order
with methods such as order having implicit parameters using the HList which is generated by the string "Eggs, Bacon" in the macro.
I am seeing some strange behaviour in which,
Calling the macro explicitly means I can't call the method on the returned object inline
If I rely on the implicit conversion which uses the fundep method, the type is erased and I seem to be casting to types without ever explicitly casting
Materializing explicitly and then calling a method is OK.
Foo.scala has the simplified macro and FooExample.scala has the strange behaviour.
Is there anyway to do this?
Foo.scala
import shapeless._
import scala.language.experimental.macros
import scala.language.implicitConversions
import scala.reflect.macros.whitebox
class Foo[L <: HList](val list: L) { self =>
def foo: Foo[L] = self
}
object Foo {
implicit def materializeFoo[L <: HList](foo: String): Foo[L] = macro FooMacro.materialize_foo_impl[L]
}
object FooMacro {
def materialize_foo_impl[L <: HList : c.WeakTypeTag](c: whitebox.Context)(foo: c.Expr[String]): c.Tree = {
import c.universe._
q"""{ new Foo($foo :: HNil) }"""
}
}
FooExample.scala
import shapeless._
import shapeless.test._
import Foo._
final class Bar[L <: HList](foo: Foo[L]) {
def bar: L = foo.list
}
object FooExample {
implicit def foo2Bar[L <: HList](foo: Foo[L]): Bar[L] = new Bar(foo)
def main(args: Array[String]) {
val expected = "test" :: HNil
val notExpected = HNil
val f1 = Foo.materializeFoo("test")
sameTyped(expected)(f1.list)
val f2 = "test".foo
sameTyped(f2.list)(expected)
sameTyped(f2.list)(notExpected)
val l1: String :: HNil = f2.list
//This actually compiles but throws a ClassCastException
//val l2: HNil = f2.list
illTyped("""Foo.materializeFoo("test").list""")
sameTyped(expected)("test".foo.list)
sameTyped(notExpected)("test".foo.list)
sameTyped(expected)(f1.bar)
illTyped("""Foo.materializeFoo("test").bar""")
//I actually just want to call "test".foo.bar and have bar have the correct type
}
}
I'm interested in creating a TypeTag manually (since 2.10M5):
object X {
import reflect.runtime.universe._
def tt[A : TypeTag](a: A) = typeTag[A] // how to do this manually?
val t = tt(List("")(_))
}
scalac -Xprint:typer <file>.scala results in
package <empty> {
object X extends scala.AnyRef {
def <init>(): X.type = {
X.super.<init>();
()
};
import scala.reflect.runtime.`package`.universe._;
def tt[A >: Nothing <: Any](a: A)(implicit evidence$1: reflect.runtime.universe.TypeTag[A]): reflect.runtime.universe.TypeTag[A] = scala.reflect.runtime.`package`.universe.typeTag[A](evidence$1);
private[this] val t: reflect.runtime.universe.TypeTag[Int => String] = X.this.tt[Int => String](((x$1: Int) => immutable.this.List.apply[String]("").apply(x$1)))({
val $u: reflect.runtime.universe.type = scala.this.reflect.runtime.`package`.universe;
val $m: $u.Mirror = scala.this.reflect.runtime.`package`.universe.runtimeMirror(this.getClass().getClassLoader());
$u.TypeTag.apply[Int => String]($m, {
final class $typecreator1 extends TypeCreator {
def <init>(): $typecreator1 = {
$typecreator1.super.<init>();
()
};
def apply[U >: Nothing <: scala.reflect.base.Universe with Singleton]($m$untyped: scala.reflect.base.MirrorOf[U]): U#Type = {
val $u: U = $m$untyped.universe;
val $m: $u.Mirror = $m$untyped.asInstanceOf[$u.Mirror];
$u.TypeRef.apply($u.ThisType.apply($m.staticModule("scala").asModuleSymbol.moduleClass), $m.staticClass("scala.Function1"), scala.collection.immutable.List.apply[$u.Type]($m.staticClass("scala.Int").asTypeSymbol.asTypeConstructor, $m.staticClass("java.lang.String").asTypeSymbol.asTypeConstructor))
}
};
new $typecreator1()
})
});
<stable> <accessor> def t: reflect.runtime.universe.TypeTag[Int => String] = X.this.t
}
}
It seems to be completely compiler magic because the types are hardcoded. Nevertheless is there a way to do this manually?
In M3 you could create a type tag in a very simple way, e.g.: TypeTag[Int](TypeRef(<scala package>, <symbol of scala.Int>, Nil)). It basically meant that once a type tag is created, it is forever bound to a certain classloader (namely the one that was used to load a symbol of scala.Int in the example above).
Back then it was fine, because we considered that we could have a one-size-fits-all mirror that accomodates all classloaders. That was convenient, because you could just write implicitly[TypeTag[T]] and the compiler would use that global mirror to instantiate a Type you requested.
Unfortunately later, based on feedback, we realized that this isn't going to work, and that we need multiple mirrors - each having its own classloader.
And then it became apparent that type tags need to be flexible, because once you write that implicitly[TypeTag[T]] the compiler doesn't have enough information what classloader you want to use. Basically there were two alternatives: 1) make type tags path-dependent on mirrors (so that one would be forced to explicitly provide a mirror every time a type tag is requested from the compiler), 2) transform type tags into type factories, capable of instantiating themselves in any mirror. Long story short, the first option didn't work, so we are where we are.
So currently type tags need to be created in quite a roundabout way, as outlined in https://github.com/scala/scala/blob/master/src/library/scala/reflect/base/TypeTags.scala#L143. You call a factory method defined in the companion TypeTag and provide two arguments: 1) a default mirror, where the type tag will be instantiated, 2) a type factory that can instantiate a type tag in arbitrary mirror. This is basically what the compiler did in the printout you've attached above.
Why I called this roundabout? Because to provide a type factory you need to manually subclass the scala.reflect.base.TypeFactory class. That's an unfortunate limitation of Scala type system on the border between function and dependently-typed methods.
Based on Get TypeTag[A] from Class[A]:
import scala.reflect.runtime.universe._
def typeToTypeTag[T](
tpe: Type,
mirror: reflect.api.Mirror[reflect.runtime.universe.type]
): TypeTag[T] = {
TypeTag(mirror, new reflect.api.TypeCreator {
def apply[U <: reflect.api.Universe with Singleton](m: reflect.api.Mirror[U]) = {
assert(m eq mirror, s"TypeTag[$tpe] defined in $mirror cannot be migrated to $m.")
tpe.asInstanceOf[U#Type]
}
})
}
For example this can be used to get the TypeTag for a part of another TypeTag:
def inside[A, B](tag: TypeTag[(A, B)]): (TypeTag[A], TypeTag[B]) = {
val tpes = tag.tpe.asInstanceOf[TypeRefApi].args
val tagA = typeToTypeTag[A](tpes(0), tag.mirror)
val tagB = typeToTypeTag[B](tpes(1), tag.mirror)
return (tagA, tagB)
}
This works in Scala 2.10.2:
scala> inside(typeTag[(Int, Double)])
res0: (reflect.runtime.universe.TypeTag[Int], reflect.runtime.universe.TypeTag[Double]) = (TypeTag[Int],TypeTag[Double])
The limitation of being tied to a particular Mirror is likely not a problem as long as you don't have multiple ClassLoaders.
Currently, there are three ways to create a TypeTag that supports generics manually. The first two depends on the scala-compiler, and with them you get type checking just like in compile time, as it is an actual code compilation:
import scala.reflect.runtime.universe._
import scala.reflect.runtime.currentMirror
import scala.tools.reflect.ToolBox
val toolbox = currentMirror.mkToolBox()
def createTypeTag(tp: String): TypeTag[_] = {
val ttree = toolbox.parse(s"scala.reflect.runtime.universe.typeTag[$tp]")
toolbox.eval(ttree).asInstanceOf[TypeTag[_]]
}
If you need a serializable TypeTag and performance isn't your main concern, the first method is the most succinct. OTOH, if your use case needs to be very performant, beware that on-the-fly compilation might take a couple of seconds to finish.
The second method performs a little better:
def createTypeTag(tp: String): TypeTag[_] = {
val ttagCall = s"scala.reflect.runtime.universe.typeTag[$tp]"
val tpe = toolbox.typecheck(toolbox.parse(ttagCall), toolbox.TYPEmode).tpe.resultType.typeArgs.head
TypeTag(currentMirror, new reflect.api.TypeCreator {
def apply[U <: reflect.api.Universe with Singleton](m: reflect.api.Mirror[U]) = {
assert(m eq mirror, s"TypeTag[$tpe] defined in $mirror cannot be migrated to $m.")
tpe.asInstanceOf[U#Type]
}
}
}
This second mode creates a typed tree and fetches the concrete types marked in the TypeTag, i.e., if your target is a List[String], it will be translated to scala.collection.immutable.List[String], since scala.List is only a type alias. That's actually the behavior expected from typeTag[List[String]].
The last method is to create a Type manually and use it to create the TypeTag. This method is error-prone, there is limited type checking, and it uses internal API. This is, however, the fastest way to manually obtain a TypeTag.
def createTypeTag(tp: String): TypeTag[_] = {
val typs = // ... manipulate the string to extract type and parameters
val typSym = currentMirror.staticClass(typs[0])
val paramSym = currentMirror.staticClass(typs[1])
val tpe = universe.internal.typeRef(NoPrefix, typSym, List(paramSym.selfType))
val ttag = TypeTag(currentMirror, new TypeCreator {
override def apply[U <: Universe with Singleton](m: Mirror[U]): U#Type = {
assert(m == currentMirror, s"TypeTag[$tpe] defined in $currentMirror cannot be migrated to $m.")
tpe.asInstanceOf[U#Type]
}
})
}
The function definition
def tt[A : TypeTag](a: A) = typeTag[A]
is just another way of writing
def tt(a: A)(implicit tag: TypeTag[A]) = tag
which means that the instance of a tag is being created implicitly by the compiler. The reasoning behind this is that Scala compiler solves the JVM's type erasure problem by hardcoding the otherwise erased type information.
In case you're not familiar with type erasure issue, it is that JVM doesn't store the type parameters information, for instance a type Seq[Set[Int]] would be seen simply as Seq[_] by the JVM and there would be no way for you to find out the missing type information with reflection.
The following implementation with a macro seems to produce type tags that are serializable and deserializable after serialization
import scala.language.experimental.macros
import scala.reflect.api
import scala.reflect.macros.blackbox
def typeToTypeTag(mirror: api.Mirror[_ <: api.Universe with Singleton])(tpe: mirror.universe.Type): mirror.universe.TypeTag[_] =
macro typeToTypeTagImpl
// def typeToTypeTag[U <: api.Universe with Singleton](mirror: api.Mirror[U])(tpe: U#Type): U#TypeTag[_] =
// macro typeToTypeTagImpl
def typeToTypeTagImpl(c: blackbox.Context)(mirror: c.Tree)(tpe: c.Tree): c.Tree = {
import c.universe._
val api = q"_root_.scala.reflect.api"
q"""
$mirror.universe.TypeTag.apply[$tpe]($mirror,
new $api.TypeCreator {
override def apply[U <: $api.Universe with _root_.scala.Singleton](m: $api.Mirror[U]): U#Type = {
assert(m == $mirror, "TypeTag[" + $tpe + "] defined in " + $mirror + " cannot be migrated to " + m + ".")
$tpe.asInstanceOf[U#Type]
}
}
)
"""
}
In scala 2.12, why none of the TypeTag created in runtime is serializable?
This macro produces a type tag tree (when typechecked)
// -Xprint:typer -Xprint-types
//scalac: performing macro expansion Macro.typeToTypeTag{(mirror: scala.reflect.api.Mirror[_ <: scala.reflect.api.Universe with Singleton])(tpe: mirror.universe.Type): mirror.universe.TypeTag[_]}(App.this{App.type}.m{scala.reflect.api.Mirror[reflect.runtime.universe.type]}){(tpe: App.m.universe.Type): App.m.universe.TypeTag[_]}(App.this{App.type}.tpe{reflect.runtime.universe.Type}){App.m.universe.TypeTag[_$2]}
scalac: App.this{App.type}.m.universe.TypeTag.apply{[T](mirror1: scala.reflect.api.Mirror[scala.reflect.api.Mirror[reflect.runtime.universe.type]#universe.type], tpec1: scala.reflect.api.TypeCreator): scala.reflect.api.Mirror[reflect.runtime.universe.type]#universe.TypeTag[T]}[App.this{App.type}.tpe{reflect.runtime.universe.Type}]{(mirror1: scala.reflect.api.Mirror[scala.reflect.api.Mirror[reflect.runtime.universe.type]#universe.type], tpec1: scala.reflect.api.TypeCreator): scala.reflect.api.Mirror[reflect.runtime.universe.type]#universe.TypeTag[reflect.runtime.universe.Type]}(App.this{App.type}.m{scala.reflect.api.Mirror[reflect.runtime.universe.type]}, {
final class $anon extends _root_.scala.reflect.api.TypeCreator {
def <init>(): <$anon: scala.reflect.api.TypeCreator> = {
$anon.super{super.type}.<init>{(): scala.reflect.api.TypeCreator}(){scala.reflect.api.TypeCreator};
(){Unit}
}{Unit};
override def apply[U <: scala.reflect.api.Universe with Singleton](m: scala.reflect.api.Mirror[U]): U#Type = {
scala.Predef.assert{(assertion: Boolean, message: => Any): Unit}(m.=={(x$1: Any): Boolean}(App.this{App.type}.m{scala.reflect.api.Mirror[reflect.runtime.universe.type]}){Boolean}, "TypeTag["{String("TypeTag[")}.+{(x$1: Any): String}(App.this{App.type}.tpe{reflect.runtime.universe.Type}){String}.+{(x$1: Any): String}("] defined in "{String("] defined in ")}){String}.+{(x$1: Any): String}(App.this{App.type}.m{scala.reflect.api.Mirror[reflect.runtime.universe.type]}){String}.+{(x$1: Any): String}(" cannot be migrated to "{String(" cannot be migrated to ")}){String}.+{(x$1: Any): String}(m{scala.reflect.api.Mirror[U]}){String}.+{(x$1: Any): String}("."{String(".")}){String}){Unit};
App.this{App.type}.tpe.asInstanceOf{[T0]T0}[U#Type]{U#Type}
}{U#Type}
};
new $anon{scala.reflect.api.TypeCreator}{(): scala.reflect.api.TypeCreator}(){scala.reflect.api.TypeCreator}
}{scala.reflect.api.TypeCreator}){scala.reflect.api.Mirror[reflect.runtime.universe.type]#universe.TypeTag[reflect.runtime.universe.Type]}
different from original
//scalac: performing macro expansion scala.reflect.api.`package`.materializeTypeTag{[T](u: scala.reflect.api.Universe): u.TypeTag[T]}[Int => String]{(u: scala.reflect.api.Universe): u.TypeTag[Int => String]}(scala.reflect.runtime.`package`.universe{reflect.runtime.universe.type}){reflect.runtime.universe.TypeTag[Int => String]}
scalac: {
val $u: reflect.runtime.universe.type = scala.reflect.runtime.`package`.universe{reflect.runtime.universe.type};
val $m: $u.Mirror = scala.reflect.runtime.`package`.universe.runtimeMirror{(cl: ClassLoader): reflect.runtime.universe.Mirror}(this{App.type}.getClass{(): Class[_ <: App.type]}(){Class[T]}.getClassLoader{(): ClassLoader}(){ClassLoader}){reflect.runtime.universe.Mirror};
$u.TypeTag.apply{[T](mirror1: scala.reflect.api.Mirror[$u.type], tpec1: scala.reflect.api.TypeCreator): $u.TypeTag[T]}[Int => String]{(mirror1: scala.reflect.api.Mirror[$u.type], tpec1: scala.reflect.api.TypeCreator): $u.TypeTag[Int => String]}($m{$u.Mirror}, {
final class $typecreator1 extends TypeCreator {
def <init>(): $typecreator1 = {
$typecreator1.super{$typecreator1.super.type}.<init>{(): scala.reflect.api.TypeCreator}(){scala.reflect.api.TypeCreator};
(){Unit}
}{Unit};
def apply[U <: scala.reflect.api.Universe with Singleton]($m$untyped: scala.reflect.api.Mirror[U]): U#Type = {
val $u: U = $m$untyped.universe{$m$untyped.universe.type};
val $m: $u.Mirror = $m$untyped.asInstanceOf{[T0]T0}[$u.Mirror]{$u.Mirror};
$u.internal.reificationSupport.TypeRef{(pre: $u.Type, sym: $u.Symbol, args: List[$u.Type]): $u.Type}($u.internal.reificationSupport.ThisType{(sym: $u.Symbol): $u.Type}($m.staticPackage{(fullName: String): $u.ModuleSymbol}("scala"{String("scala")}){$u.ModuleSymbol}.asModule.moduleClass{$u.Symbol}){$u.Type}, $m.staticClass{(fullName: String): $u.ClassSymbol}("scala.Function1"{String("scala.Function1")}){$u.ClassSymbol}, scala.collection.immutable.List.apply{[A](elems: A*): List[A]}[$u.Type]{(elems: $u.Type*): List[$u.Type]}($m.staticClass{(fullName: String): $u.ClassSymbol}("scala.Int"{String("scala.Int")}){$u.ClassSymbol}.asType.toTypeConstructor{$u.Type}, $u.internal.reificationSupport.TypeRef{(pre: $u.Type, sym: $u.Symbol, args: List[$u.Type]): $u.Type}($u.internal.reificationSupport.SingleType{(pre: $u.Type, sym: $u.Symbol): $u.Type}($m.staticPackage{(fullName: String): $u.ModuleSymbol}("scala"{String("scala")}){$u.ModuleSymbol}.asModule.moduleClass.asType.toTypeConstructor{$u.Type}, $m.staticModule{(fullName: String): $u.ModuleSymbol}("scala.Predef"{String("scala.Predef")}){$u.ModuleSymbol}){$u.Type}, $u.internal.reificationSupport.selectType{(owner: $u.Symbol, name: String): $u.TypeSymbol}($m.staticModule{(fullName: String): $u.ModuleSymbol}("scala.Predef"{String("scala.Predef")}){$u.ModuleSymbol}.asModule.moduleClass{$u.Symbol}, "String"{String("String")}){$u.TypeSymbol}, scala.collection.immutable.Nil{List[Nothing]}){$u.Type}){List[$u.Type]}){$u.Type}
}{$u.Type}
};
new $typecreator1{$typecreator1}{(): $typecreator1}(){$typecreator1}
}{$typecreator1}){$u.TypeTag[Int => String]}
}{$u.TypeTag[Int => String]}
but it seems to be working
val tpe: Type = typeOf[Int => String]
val ttag: TypeTag[_] = typeToTypeTag(m)(tpe)
ttag.tpe =:= tpe // true
For original type tag tree the methods scala.reflect.api.materializeTypeTag, scala.tools.reflect.FastTrack#context2taggers (L63), scala.reflect.reify.Taggers#materializeTypeTag, scala.reflect.reify.reifyType should be considered:
https://gist.github.com/DmytroMitin/e9119ea0842479ec0c897660812e38f4 (static type T instead of tpe: Type, compiler internals).
It's possible that original type tag tree can't be restored from runtime type tpe: Type on contrary to static T.