This is a follow-up to my previous question:
Suppose I create the following test converter.scala:
trait ConverterTo[T] {
def convert(s: String): Option[T]
}
object Converters {
implicit val toInt: ConverterTo[Int] =
new ConverterTo[Int] {
def convert(s: String) = scala.util.Try(s.toInt).toOption
}
}
class A {
import Converters._
def foo[T](s: String)(implicit ct: ConverterTo[T]) = ct.convert(s)
}
Now when I tried to call foo in REPL it fails to compile:
scala> :load converter.scala
Loading converter.scala...
defined trait ConverterTo
defined module Converters
defined class A
scala> val a = new A()
scala> a.foo[Int]("0")
<console>:12: error: could not find implicit value for parameter ct: ConverterTo[Int]
a.foo[Int]("0")
^
import Converters._ in class A does not cut it. You can remove it and the code will still compile. The moment the compiler needs to find in actual implicit is not in class A, where foo is just declared.
The compiler needs to find a ConverterTo[Int] in implicit scope at the moment you call a.foo[Int](..) that is in the REPL. So this is where the import needs to be.
Had object Converters and trait ConverterTo been named the same (so there would be a companion object) the import would not be needed.
Related
The following code example shows the core of my question:
// This is the base trait that the classes are extending
trait Operation[T] {
def operate(x: T): T
}
// Here are 2 case classes that exist for the sole purpose of being
// the generic type for the classes I'm making
case class CaseClass1(field1_1: String, field1_2: String, field1_3: String)
case class CaseClass2(field2_1: String, field2_2: String, field2_3: String)
// These are the 2 classes that extend my basic trait, implementing the operate
// method with some kind of logic
class FillField1 extends Operation[CaseClass1] {
def operate(input: CaseClass1): CaseClass1 = input.copy(field1_3 = "haha")
}
class FillField2 extends Operation[CaseClass2] {
def operate(input: CaseClass2): CaseClass2 = input.copy(field2_2 = "hoho")
}
import scala.reflect.runtime.universe._
// This is a function that prints out the typetag information currently available
def someFunc[T: TypeTag](x: Operation[T]): Unit = {
println(s"TypeTag is: ${typeOf[T]}")
}
// Here I'm creating a sequence of Operations, but they can have any generic
// type parameter
val someSeq: Seq[Operation[_]] = Seq(new FillField1, new FillField2)
someSeq.map(someFunc(_))
/*
Output:
TypeTag is: _$1
TypeTag is: _$1
*/
someFunc(new FillField1)
/*
Output:
TypeTag is: CaseClass1
*/
As you can see, when I call someFunc(new fillField1) I can properly find my typetag at runtime. But when I'm using someSeq, which is a Sequence that can contain multiple types of classes I can't get the typetag I need at runtime. Is this because you lose that information at runtime?
How can I get the proper typetag at runtime? So how could I get as output TypeTag is: CustomClass1 and TypeTag is: CustomClass2 when I'm using that Seq[Operation[_]]?
I'm working on an Apache Spark project where we have a structure similar to this and when I'm using that sequence I'm getting an issue that the TypeTag points to an unknown class, _$10 (or whatever name the compiler made for my typetag), instead of the actual TypeTag which would be CustomClass1 or CustomClass2...
What TypeTag mostly does is not runtime reflection but persisting some information (a type) from compile time to runtime.
Seq is a homogeneous collection (i.e. all its elements have the same type). In Seq(new FillField1, new FillField2) both elements have type Operation[_]. So when someFunc is applied T is inferred to be _ aka _$1 (i.e. unknown argument of existential type Operation[_]).
So one option is to use a heterogeneous collection (HList). Then the elements can have different types, these types can be captured from compile time to runtime, the types can be handled at runtime
import shapeless.{HNil, Poly1}
object someFuncPoly extends Poly1 {
implicit def cse[T: TypeTag, O](implicit ev: O <:< Operation[T]): Case.Aux[O, Unit] =
at(x => someFunc(x))
}
def someFunc[T: TypeTag](x: Operation[T]): Unit = println(s"Type is: ${typeOf[T]}")
(new FillField1 :: new FillField2 :: HNil).map(someFuncPoly)
//Type is: CaseClass1
//Type is: CaseClass2
Another option is to use runtime reflection (i.e. what TypeTag doesn't do)
import scala.reflect.runtime.universe._
import scala.reflect.runtime
val runtimeMirror = runtime.currentMirror
def someFunc(x: Operation[_]): Unit = {
val xSymbol = runtimeMirror.classSymbol(x.getClass)
val operationSymbol = xSymbol.baseClasses(1)// or just typeOf[Operation[_]].typeSymbol if you know Operation statically
val extendee = xSymbol.typeSignature/*toType*/.baseType(operationSymbol)
println(s"Type is: ${extendee.typeArgs.head}")
}
someSeq.map(someFunc(_))
//Type is: CaseClass1
//Type is: CaseClass2
Another implementation is
def someFunc(x: Operation[_]): Unit = {
val xSymbol = runtimeMirror.classSymbol(x.getClass)
val operationSymbol = xSymbol.baseClasses(1).asClass
val operationParamType = operationSymbol.typeParams(0).asType.toType
println(s"Type is: ${operationParamType.asSeenFrom(xSymbol.toType, operationSymbol)}")
}
One more option is magnet pattern (1 2 3 4 5 6 7)
trait Magnet[T] {
def someFunc: Unit
}
import scala.language.implicitConversions
implicit def operationToMagnet[T: TypeTag](x: Operation[T]): Magnet[T] = new Magnet[T] {
override def someFunc: Unit = println(s"TypeTag is: ${typeOf[T]}")
}
def someFunc[T: TypeTag](x: Operation[T]): Unit = (x: Magnet[T]).someFunc
Seq[Magnet[_]](new FillField1, new FillField2).map(_.someFunc)
// TypeTag is: CaseClass1
// TypeTag is: CaseClass2
Alternatively you can move someFunc inside Operation, move TypeTag from the method to the class and make Operation an abstract class
abstract class Operation[T: TypeTag] {
def operate(x: T): T
def someFunc: Unit = {
println(s"TypeTag is: ${typeOf[T]}")
}
}
(new FillField1).someFunc //TypeTag is: CaseClass1
(new FillField2).someFunc //TypeTag is: CaseClass2
someSeq.map(_.someFunc)
//TypeTag is: CaseClass1
//TypeTag is: CaseClass2
Is it possible to do something like below? We have a ConcreteType which has trait definition and Object definition. At runtime I want to create Object version to access get method of some ConcreteType. Note code below throws cannot cast to SomeTrait exception at asInstanceOf. Also I am using some library that uses format below so have no choice but to work with this construct.
trait SomeType
trait SomeTrait[T <: SomeType] {
def get(i: Int): Option[SomeType]
}
trait ConcreteType extends SomeType
object ConcreteType extends SomeTrait[ConcreteType]
def temp[T <: SomeType]()(implicit tag: ClassTag[T]): Option[T] = {
asInstanceOf[SomeTrait[T]].get(1)
}
I'm not sure what you're doing (I asked several questions in comments), but yes, you can get the object from ClassTag
import scala.reflect.ClassTag
import scala.reflect.runtime
def temp[T <: SomeType]()(implicit tag: ClassTag[T]): Option[/*T*/SomeType] = {
val runtimeMirror = runtime.currentMirror
val classSymbol = runtimeMirror.classSymbol(tag.runtimeClass)
val companionSymbol = classSymbol.companion.asModule
val companionModuleMirror = runtimeMirror.reflectModule(companionSymbol)
val companionInstance = companionModuleMirror.instance
companionInstance.asInstanceOf[SomeTrait[T]].get(1)
}
temp[ConcreteType]()
I was playing with implicits but I got some behaviour that I don't understand.
I defined a simple class and its companion object (just to make a quick test on implicits) as follows
class SimpleNumber(val numb: Int) {
def sum(n: Int) = numb + n
}
object SimpleNumber {
implicit def stringToInt(s: String) = s.toInt
}
The method stringToInt is supposed to work in the event I call the sum method by passing a string instead of an int, so that it can be converted to an int (it's just for testing purposes, so I don't need to check errors or exceptions).
If I paste the above code in the repl (:paste), I get this warning
warning: implicit conversion method stringToInt should be enabled
by making the implicit value scala.language.implicitConversions visible.
This can be achieved by adding the import clause 'import scala.language.implicitConversions'
or by setting the compiler option -language:implicitConversions.
So I moved to VSCode and pasted in the same code, to see if I could get some more info via metals plugin, but that warning doesn't even pop out. Then I created a new object that extends the App trait to test the code like this
object TestDriver extends App{
val a = new SimpleNumber(4)
println(a.sum("5"))
}
but I received the following error
type mismatch; found : String("5") required: Int
I tried importing the implicitConversions as the repl suggested, first in the companion object and then in the TestDriver object, but to no avail. Then I imported the implicit method directly in the TestDriver object, and that worked.
import SimpleNumber.stringToInt
object TestDriver extends App{
val a = new SimpleNumber(4)
println(a.sum("5"))
}
Why doesn't the import scala.language.implicitConversions work as I thought it would? I'm using scala 2.13.3
Why doesn't the import scala.language.implicitConversions work as I thought it would?
import scala.language.implicitConversions is just to signal that implicit conversions are defined in a code, not to bring specific ones to a scope. You can do
import SimpleNumber._
to bring stringToInt to the local scope.
Where does Scala look for implicits?
Should I use method overload then?
Depends on your goal. Implicit conversions (which are normally not recommended) are not just for method overloading. In your example String can be used in all places where Int is expected (this is just for example, normally standard types like Int, String, Function shouldn't be used for implicits, use custom types for implicits). If you used implicit conversions just for method overloading then yes, you should prefer the latter. Don't forget that method overloading can be done not only as
class SimpleNumber(val numb: Int) {
def sum(n: Int): Int = numb + n
def sum(n: String): Int = numb + n.toInt
}
but also with a type class
class SimpleNumber(val numb: Int) {
def sum[A: ToInt](n: A): Int = numb + n.toInt
}
trait ToInt[A] {
def toInt(a: A): Int
}
object ToInt {
implicit val int: ToInt[Int] = identity
implicit val str: ToInt[String] = _.toInt
}
implicit class ToIntOps[A](val a: A) extends AnyVal {
def toInt(implicit ti: ToInt[A]): Int = ti.toInt(a)
}
or magnet
import scala.language.implicitConversions
import Predef.{augmentString => _, _} // to avoid implicit ambiguity
class SimpleNumber(val numb: Int) {
def sum(n: ToInt): Int = numb + n.toInt()
}
trait ToInt {
def toInt(): Int
}
object ToInt {
implicit def fromInt(i: Int): ToInt = () => i
implicit def fromStr(s: String): ToInt = () => s.toInt
}
Notice that you don't have to import ToInt.
Overloading methods based on generics
Type erasure problem in method overloading
Consider a trait that performs "encoding" of arbitrary objects:
trait Encoder[R] {
def encode(r: R): Array[Byte]
}
Assuming encoding of primitive types is known and custom types can be encoded by defining up a "serializer":
trait Serializer[T] {
def serialize(r: T): Array[Byte]
}
we can implement a macro for case class encoding by simply looping the fields and looking up type serializers implicitly. Here's a dummy implementation that looks up the serializer for R itself (in reality we look up serializers for case class field types):
object Encoder {
implicit def apply[R <: Product]: Encoder[R] = macro applyImpl[R]
def applyImpl[R: c.WeakTypeTag](c: blackbox.Context): c.Expr[Encoder[R]] = {
import c.universe._
c.Expr[Encoder[R]](q"""
new ${weakTypeOf[Encoder[R]]} {
override def encode(r: ${weakTypeOf[R]}): Array[Byte] =
implicitly[_root_.Serializer[${weakTypeOf[R]}]].serialize(r)
}
""")
}
}
Now define a base "processor":
abstract class BaseProcessor[R: Encoder] {
def process(r: R): Unit = {
println(implicitly[Encoder[R]].encode(r).length)
}
}
And try to use it:
case class Record(i: Int)
object Serializers {
implicit def recordSerializer: Serializer[Record] =
(r: Record) => Array.emptyByteArray
}
import Serializers._
class Processor extends BaseProcessor[Record]
This fails to compile with:
// [error] Loader.scala:10:22: could not find implicit value for parameter e: Serializer[Record]
// [error] class Processor extends BaseProcessor[Record]
// [error] ^
// [error] one error found
However the following do compile:
class Processor extends BaseProcessor[Record]()(Encoder[Record]) // Compiles!
object x { class Processor extends BaseProcessor[Record] } // Compiles!
I can't really understand why this happens, looks like it has something to do with the Processor definition being a top level definition, since as soon as I move it inside a class/object everything works as expected. Here's one more example that fails to compile:
object x {
import Serializers._ // moving the import here also makes it NOT compile
class Processor extends BaseProcessor[Record]
}
Why does this happen and is there any way to make it work?
To make it work you can try one of the following:
1) add context bound
implicit def apply[R <: Product : Serializer]: Encoder[R] = macro applyImpl[R]
def applyImpl[R: c.WeakTypeTag](c: blackbox.Context)(serializer: c.Expr[Serializer[R]]): c.Expr[Encoder[R]] = {
import c.universe._
c.Expr[Encoder[R]](q"""
new Encoder[${weakTypeOf[R]}] {
override def encode(r: ${weakTypeOf[R]}): Array[Byte] =
$serializer.serialize(r)
}
""")
}
2) make macro whitebox
3) put implicit instance recordSerializer: Serializer[Record] to the companion object of Record rather than some object Serializers to be imported
4) maybe your example is easier than actual use case but now it seems you don't need macros
implicit def apply[R <: Product : Serializer]: Encoder[R] = new Encoder[R] {
override def encode(r: R): Array[Byte] = implicitly[Serializer[R]].serialize(r)
}
The use cases for implicit macros is supposed to be the so-called "materialisation" of type class instances.
Unfortunately, the example in the documentation is a bit vague on how that is achieved.
Upon being invoked, the materializer can acquire a representation of T and generate the appropriate instance of the Showable type class.
Let's say I have the following trait ...
trait PrettyPrinter[T]{
def printed(x:T) : String
}
object PrettyPrinter{
def pretty[T](x:T)(implicit pretty:PrettyPrinter[T]) = pretty printed x
implicit def prettyList[T](implicit pretty :PrettyPrinter[T]) = new PrettyPrinter[List[T]] {
def printed(x:List[T]) = x.map(pretty.printed).mkString("List(",", ",")")
}
}
and three test classes
class A(val x:Int)
class B(val x:Int)
class C(val x:Int)
Now I understand that instead of writing the following boilerplate
implicit def aPrinter = new PrettyPrinter[A] {def printed(a:A) = s"A(${a.x})"}
implicit def bPrinter = new PrettyPrinter[B] {def printed(b:B) = s"B(${b.x})"}
implicit def cPrinter = new PrettyPrinter[C] {def printed(c:C) = s"C(${c.x})"}
we should be able to add
implicit def materialise[T] : PrettyPrinter[T] = macro implMaterialise[T]
def implMaterialise[T](c:blackbox.Context):c.Expr[PrettyPrinter[T]] = {
import c.universe._
???
}
to the object PrettyPrinter{...} which then generates the corresponding PrettyPrinters on demand ... how? How do I actually get that "representation of T"?
If I try c.typeOf[T], for example, "No TypeTag available for T".
UPDATE
Trying to use class tags doesn't seem to work either.
implicit def materialise[T:ClassTag] : PrettyPrinter[T] = macro implMaterialise[T]
def implMaterialise[T:ClassTag](c:blackbox.Context):c.Expr[PrettyPrinter[T]] = {
import c.universe._
???
}
results in
Error:(17, 69) macro implementations cannot have implicit parameters other than WeakTypeTag evidences
implicit def materialise[T:ClassTag] : PrettyPrinter[T] = macro implMaterialise[T]
^
update2
Interestingly, using WeakTypeTags doesn't really change anything as
implicit def materialise[T:WeakTypeTag]: PrettyPrinter[T] = macro implMaterialise[T]
def implMaterialise[T](c:blackbox.Context)(implicit evidence : WeakTypeTag[T]):c.Expr[PrettyPrinter[T]]
= {
import c.universe._
???
}
will result in
Error:(18, 71) macro implementations cannot have implicit parameters other than WeakTypeTag evidences
implicit def materialise[T:WeakTypeTag]: PrettyPrinter[T] = macro implMaterialise[T]
^
How do I actually get that "representation of T"?
You need to use c.WeakTypeTag, as hinted at by the compiler message you found in your "UPDATE" section.
This project has a working example that you can adapt: https://github.com/underscoreio/essential-macros/blob/master/printtype/lib/src/main/scala/PrintType.scala
object PrintTypeApp extends App {
import PrintType._
printSymbol[List[Int]]
}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
import scala.util.{ Try => ScalaTry }
object PrintType {
// Macro that generates a `println` statement to print
// declaration information of type `A`.
//
// This only prints meaningful output if we can inspect
// `A` to get at its definition:
def printSymbol[A]: Unit =
macro PrintTypeMacros.printTypeSymbolMacro[A]
}
class PrintTypeMacros(val c: Context) {
import c.universe._
def printTypeSymbolMacro[A: c.WeakTypeTag]: c.Tree =
printSymbol(weakTypeOf[A].typeSymbol, "")
}