Scala fiddle here
import scala.reflect.ClassTag
import scala.language.existentials
class SomeClass[T <: SomeClass[T, R], R] extends EarlyInit[T, R] {}
trait EarlyInit[T <: SomeClass[T, R], R] {
self: SomeClass[T, R] =>
val clazz = getClass.getSuperclass
val potentialFields = clazz.getDeclaredClasses.toList
potentialFields.foreach {
field => {
// This correctly prints out fields of object members.
// but invokation throws an error every time.
println(s"${field.getName}")
}
}
}
class SomeThing extends SomeClass[SomeThing, Any] {
val stringTest = "test"
object name
object test
}
object SomeThing extends SomeThing {}
val x = SomeThing
How do I access and initialise object members(name and test) with reflection?
You can achieve this with scala reflection API:
import scala.reflect.runtime.universe._
class SomeClass[T <: SomeClass[T, R]: TypeTag, R] extends EarlyInit[T] {}
class EarlyInit[T: TypeTag] {
val mirror = runtimeMirror(this.getClass.getClassLoader)
val reflection = mirror.reflect(this)
typeTag[T].tpe.members.filter(_.isModule).foreach(m => reflection.reflectModule(m.asModule).instance)
}
class SomeThing extends SomeClass[SomeThing, Any] {
val stringTest = "test"
object name {
println("name initialized")
}
object test {
println("test initialized")
}
}
object SomeThing extends SomeThing {}
val x = SomeThing
You need to make EarlyInit a class in order to be able to get a TypeTag evidence. Then you just search for all modules and access them (they would be initialized in the process)
Update
You can also simplify EarlyInit a bit and get rid of type parameter. You actually can get a Type of this directly from mirror:
trait EarlyInit {
val mirror = runtimeMirror(this.getClass.getClassLoader)
val reflection = mirror.reflect(this)
mirror
.classSymbol(this.getClass)
.toType
.members
.filter(_.isModule)
.foreach(m => reflection.reflectModule(m.asModule).instance)
}
Related
I want to leverage Scala reflection to implicitly pass a ClassTag.
There are plenty solutions on StackOverflow on how to accomplish this.
import scala.reflect.ClassTag
// animal input
trait AnimalInput {}
case class DogInput() extends AnimalInput
case class CatInput() extends AnimalInput
// animals
trait Animal[T <: AnimalInput] {
def getInput(implicit ct: ClassTag[T]): Class[T] = {
ct.runtimeClass.asInstanceOf[Class[T]]
}
}
object Dog extends Animal[DogInput] {}
object Cat extends Animal[CatInput] {}
I can test that this is working well:
println(Dog.getInput) // Success: "DogInput"
println(Cat.getInput) // Success: "CatInput"
The problem is, the second I reference these objects in any collection, I run into trouble:
// Failure: "No ClassTag available for animal.T"
List(Dog, Cat).foreach(animal => println(animal.getInput))
I think I understand why this is happening but I'm not sure how to work around it.
Thank you in advance for your help!
Actually, I can't reproduce No ClassTag available for animal.T. Your code compiles and runs in Scala 2.13.9:
https://scastie.scala-lang.org/DmytroMitin/lonnNg0fR1qb4Lg7ChDBqg
Maybe by failure you meant that for collection you don't receive classes DogInput, CatInput.
Actually, I managed to reproduce No ClassTag available for animal.T for type member T <: AnimalInput rather than type parameter: https://scastie.scala-lang.org/v79Y37eDRXuoauWA9b8uhQ
This question is very close to recent question Trying to extract the TypeTag of a Sequence of classes that extend a trait with different generic type parameters
See the reasons there.
Either use a heterogeneous collection
object classPoly extends Poly1 {
implicit def cse[T <: AnimalInput : ClassTag, A](implicit
ev: A <:< Animal[T]
): Case.Aux[A, Class[T]] =
at(_ => classTag[T].runtimeClass.asInstanceOf[Class[T]])
}
(Dog :: Cat :: HNil).map(classPoly).toList.foreach(println)
// class DogInput
// class CatInput
or use runtime reflection
trait Animal[T <: AnimalInput] {
def getInput: Class[T] = {
val classSymbol = runtimeMirror.classSymbol(this.getClass)
val animalSymbol = typeOf[Animal[_]].typeSymbol
val extendeeType = classSymbol.typeSignature.baseType(animalSymbol)
val extenderSymbol = extendeeType.typeArgs.head.typeSymbol.asClass
runtimeMirror.runtimeClass(extenderSymbol).asInstanceOf[Class[T]]
}
}
List(Dog, Cat).foreach(animal => println(animal.getInput))
// class DogInput
// class CatInput
The easiest is
trait Animal[T <: AnimalInput] {
def getInput: Class[T]
}
object Dog extends Animal[DogInput] {
val getInput = classOf[DogInput]
}
object Cat extends Animal[CatInput] {
val getInput = classOf[CatInput]
}
One more option is magnet pattern (1 2 3 4 5 6)
trait AnimalMagnet[T <: AnimalInput] {
def getInput: Class[T]
}
import scala.language.implicitConversions
implicit def animalToMagnet[A, T <: AnimalInput : ClassTag](a: A)(implicit
ev: A <:< Animal[T]
): AnimalMagnet[T] = new AnimalMagnet[T] {
override def getInput: Class[T] = classTag[T].runtimeClass.asInstanceOf[Class[T]]
}
List[AnimalMagnet[_]](Dog, Cat).foreach(animal => println(animal.getInput))
//class DogInput
//class CatInput
Also you can move ClassTag implicit from the method to the trait (and make the trait an abstract class)
abstract class Animal[T <: AnimalInput](implicit ct: ClassTag[T]) {
def getInput: Class[T] = ct.runtimeClass.asInstanceOf[Class[T]]
}
List(Dog, Cat).foreach(animal => println(animal.getInput))
// class DogInput
// class CatInput
I'm tying to create a typesafe wrapper around lwjgl. Most importantly, I want it to be a compile time error to pass the wrong constants to a function, e.g. calling glEnable(GL_ARRAY_BUFFER). This whould be rather easy if it weren't for different contexts supporting different constants for the same function.
I figured I'd use type classes to model which constants can be passed to which function. I've got a solution but admittedly it's a bit ugly and fails to apply a certain implicit:
trait GlConst { val glConst: Int }
trait GlConstCompanion[C <: GlConst] { val instance: C }
class GlDepthTest private () extends GlConst { val glConst = GL_DEPTH_TEST }
object GlDepthTest extends GlConstCompanion[GlDepthTest] {
val instance = new GlDepthTest
}
class GlLineSmooth private () extends GlConst { val glConst = GL_LINE_SMOOTH }
object GlLineSmooth extends GlConstCompanion[GlLineSmooth] {
val instance = new GlLineSmooth
}
class GlArrayBuffer private () extends GlConst { val glConst = GL_ARRAY_BUFFER }
object GlArrayBuffer extends GlConstCompanion[GlArrayBuffer] {
val instance = new GlArrayBuffer
}
// type class for arguments to glEnable
trait GlEnableCap[T <: GlConst] extends GlConst
object GlContext33 {
implicit object GlDepthTestIsEnableCap extends GlEnableCap[GlDepthTest] {
val glConst = GlDepthTest.instance.glConst
}
implicit object GlLineSmoothIsEnableCap extends GlEnableCap[GlLineSmooth] {
val glConst = GlLineSmooth.instance.glConst
}
def glEnable[T <: GlConst : GlEnableCap](t: T): Unit = println(implicitly[GlEnableCap[T]].glConst)
}
object Test extends App {
import GlContext33._
implicit def constComp2Const[C <: GlConst](cc: GlConstCompanion[C]): C = cc.instance
// works
glEnable(GlDepthTest.instance)
// fails to apply implicit glConstComp2Comp
glEnable(GlDepthTest)
// fails intentionally
glEnable(GlArrayBuffer)
}
Is there a way to get the implicit to work? Or is there an even better way to wrap OpenGL's constants?
As a rule of thumb, don't use implicits if you don't have to.
In this case you can solve it just as well using only type bounds:
// Start writing your ScalaFiddle code here
val GL_DEPTH_TEST = 1
val GL_LINE_SMOOTH = 1
val GL_ARRAY_BUFFER = 1
trait GlConstCap
trait GlEnableConstCap extends GlConstCap
trait GlBufferConstCap extends GlConstCap
trait GlConst[C <: GlConstCap] { val value: Int }
object GlDepthTest extends GlConst[GlEnableConstCap] {
val value = GL_DEPTH_TEST
}
object GlLineSmooth extends GlConst[GlEnableConstCap] {
val value = GL_LINE_SMOOTH
}
object GlArrayBuffer extends GlConst[GlBufferConstCap] {
val value = GL_ARRAY_BUFFER
}
object GlContext33 {
def glEnable[T <: GlConst[GlEnableConstCap]](t: T): Unit = println(t.value)
}
object Test extends App {
import GlContext33._
// works
glEnable(GlDepthTest)
// fails to apply implicit glConstComp2Comp
glEnable(GlDepthTest)
// fails intentionally
glEnable(GlArrayBuffer)
}
Try it out!
Note: You might want to add contravariance to C in GlConst if you want to create deeper inheritance structures of GlConstCap.
I hope this helps.
trait Encoder[From, To] {
def encode(x: From): To
}
object Encoder {
implicit val thingToString: Encoder[Thing, String] = new Encoder[Thing, String] {
def encode(x: Thing): String = x.toString
}
}
trait Config {
type Repr
}
class MyConfig extends Config { type Repr = String }
//class ConcreteConfig { type Repr = String }
class Api[T](val config: Config) {
def doSomething(value: T)(implicit encoder: Encoder[T, config.Repr]): Unit = {}
}
case class Thing(a: Int)
object Test extends App {
import Encoder._
val api = new Api[Thing](new MyConfig)
api.doSomething(Thing(42))
}
The call to api.doSomething fails to compile:
could not find implicit value for parameter encoder: Encoder[Thing,Test.api.config.Repr]
If I change the signature of class Api[T]'s constructor so that it takes a ConcreteConfig, then the compiler can tell that config.Repr == String and the implicit lookup succeeds. But this wouldn't really work for my use case.
Is there any other way to guide the implicit lookup? Am I losing type info because I'm missing a type refinement or something?
This can't be achieved since config.Repr is not a stable path. The compiler can't determine that config.Repr = String, even though the runtime value of config is of type MyConfig.
This is going to work only if you can provide concrete config instance as a type parameter in Api, which will tell compiler what is the exact type of Repr:
class Api[T, C <: Config](val config: C) {
def doSomething(value: T)(implicit encoder: Encoder[T, config.Repr]): Unit = {}
}
object Test extends App {
import Encoder._
val api = new Api[Thing, MyConfig](new MyConfig)
api.doSomething(Thing(42))
}
Infer types one step at a time. Also, I don't see any reason for using value-dependent types here (or, for that matter, anywhere; that's a different question though).
trait Encoder[From, To] {
def encode(x: From): To
}
object Encoder {
implicit val thingToString: Encoder[Thing, String] = new Encoder[Thing, String] {
def encode(x: Thing): String = x.toString
}
}
trait Config {
type Repr
}
class MyConfig extends Config { type Repr = String }
// class ConcreteConfig { type Repr = String }
case class Api[T, C <: Config](val config: C) {
def doSomething(value: T)(implicit encoder: Encoder[T, C#Repr]): Unit = ()
}
case class ApiFor[T]() {
def withConfig[C <: Config](config: C): Api[T,C] = Api(config)
}
case class Thing(a: Int)
object Test extends App {
import Encoder._
val api = ApiFor[Thing] withConfig new MyConfig
// compiles!
api.doSomething(Thing(42))
}
Given the code
object Renderer {
sealed abstract class BasicRender
case class RenderImages(img: Array[File]) extends BasicRender
case class RenderVideo(video: File) extends BasicRender
def rendererFor[T <: BasicRender : Manifest, Z <: Render.RenderingContext](ctx: Z): Option[Render[T]] = {
val z = manifest[T].erasure
if (z == classOf[RenderImages]) {
Some(new ImagesRenderer(ctx.asInstanceOf[ImagesContext])) // .asInstanceOf[Render[T]])
} else
if (z == classOf[RenderVideo]) {
Some(new VideoRenderer(ctx.asInstanceOf[VideoContext])) // .asInstanceOf[Render[T]])
} else {
None
}
}
private class ImagesRenderer(ctx: ImagesContext) extends Render[RenderImages] {
override def renderJSON(json: String)(implicit jsCtx: PhantomJsContext) = {
None
}
}
private class VideoRenderer(ctx: VideoContext) extends Render[RenderVideo] {
override def renderJSON(json: String)(implicit jsCtx: PhantomJsContext) = {
None
}
}
}
trait Render[+Out] {
def renderJSON(json: String)(implicit jsCtx: PhantomJsContext): Option[Out]
}
I made Render trait covariant for it's type parameter, so if
RenderImages <: BasicRender
then
ImagesRenderer <: Render[RenderImages]
But it looks like the compiler is not able to infer the type of renderer in rendererFor, so I need to add explicit class casting like
Some(new ImagesRenderer(ctx.asInstanceOf[ImagesContext]).asInstanceOf[Render[T]])
what is wrong with my reasoning here?
As explained by Daniel C. Sobral, your problem here is that you are instantiating the different renderers dynamically, in a way that does not capture in the type system the relation between ctx and the result type of rendererFor.
One common way to solve this kind of issues is to use a type class:
import java.io.File
class PhantomJsContext
trait Renderer[+Out] {
def renderJSON(json: String)(implicit jsCtx: PhantomJsContext): Option[Out]
}
trait RendererFactory[ContextType, ResultType] {
def buildRenderer( ctx: ContextType ): Renderer[ResultType]
}
object Renderer {
case class RenderImages(img: Array[File])
case class RenderVideo(video: File)
trait ImagesContext
trait VideoContext
def rendererFor[ContextType, ResultType](ctx: ContextType)( implicit factory: RendererFactory[ContextType, ResultType] ): Renderer[ResultType] = {
factory.buildRenderer( ctx )
}
class ImagesRenderer(ctx: ImagesContext) extends Renderer[RenderImages] {
def renderJSON(json: String)(implicit jsCtx: PhantomJsContext) = ???
}
implicit object ImagesRendererFactory extends RendererFactory[ImagesContext, RenderImages] {
def buildRenderer( ctx: ImagesContext ) = new ImagesRenderer( ctx )
}
class VideoRenderer(ctx: VideoContext) extends Renderer[RenderVideo] {
def renderJSON(json: String)(implicit jsCtx: PhantomJsContext) = ???
}
implicit object VideoRendererFactory extends RendererFactory[VideoContext, RenderVideo] {
def buildRenderer( ctx: VideoContext ) = new VideoRenderer( ctx )
}
}
You can easily check in the REPL that the correct types are returned:
scala> lazy val r1 = Renderer.rendererFor( new Renderer.ImagesContext {} )
r1: Renderer[Renderer.RenderImages] = <lazy>
scala> :type r1
Renderer[Renderer.RenderImages]
scala> lazy val r2 = Renderer.rendererFor( new Renderer.VideoContext {} )
r2: Renderer[Renderer.RenderVideo] = <lazy>
scala> :type r2
Renderer[Renderer.RenderVideo]
T is not to be inferred: it is a parameter that is passed to the method, and there isn't really any guarantee that what is being returned is an Option[Render[T]]. For instance, assume you pass RenderImages and it returns a VideoRenderer, then it would obviously be wrong.
Now, the if conditionals you put in there might prevent that from happening, but none of that is used by the compiler to figure out whether you are returning the right type or not.
I would like to store some objects from different type hierarchy into List[Any] or similar container, but perform implicit conversions on them later on to do something like type class.
Here is an example:
abstract class Price[A] {
def price(a: A): Int
}
trait Car
case class Prius(year: Int) extends Car
trait Food
case class FriedChicken() extends Food
object Def {
// implicit object AnyPrices extends Price[Any] {
// def price(any: Any) = 0
// }
// implicit object PriusPrices extends Price[Prius] {
// def price(car: Prius) = 100
// }
implicit object CarPrices extends Price[Car] {
def price(car: Car) = 100
}
implicit object FoodPrices extends Price[Food] {
def price(food: Food) = 5
}
}
def implicitPrice[A: Price](x: A) = implicitly[Price[A]].price(x)
import Def._
val stuff: List[Any] = List(Prius(2010), FriedChicken())
stuff map { implicitPrice(_) }
The above code throws an error as follows:
error: could not find implicit value for evidence parameter of type Price[Any]
stuff map { implicitPrice(_) }
^
If you uncomment AnyPrices, you'd get List(0,0), but that's not what I am expecting.
Do I have to store the manifest into the list for this to work?
Also, List(Prius(2010)) map { implicitPrice(_) } doesn't work either because it wants Price[Prius] and Price[Car] isn't good enough. Is there a way to make it more flexible?
So, looks like I can't get a type class once the objects are reduced to Any. My attempt of using Manifest also failed, since there seems to be no way for me to cast an Any into T even if I have the Manifest[T] object.
import reflect.Manifest._
def add [A, B >: A](stuff: A, list: List[(B, Manifest[_])])(implicit m: Manifest[A]) = (stuff, m) :: list
val stuff2 = add(Prius(2000), add(FriedChicken(), Nil))
stuff2 map { x =>
val casted = x._2.erasure.cast(x._1)
implicitPrice(casted)
}
gives me
error: could not find implicit value for evidence parameter of type Price[Any]
so it seems like I have to resolve things into Price before I stick them into List:
abstract class Price[A] {
def price(a: Any): Int
}
trait Car
case class Prius(year: Int) extends Car
trait Food
case class FriedChicken() extends Food
object Def {
implicit object PriusPrices extends Price[Prius] {
def price(car: Any) = 100
}
implicit object FriedChickenPrices extends Price[FriedChicken] {
def price(food: Any) = 5
}
}
import Def._
def add [A, B >: A](stuff: A, list: List[(B, Price[_])])(implicit p: Price[A]) = (stuff, p) :: list
val stuff = add(Prius(2000), add(FriedChicken(), Nil))
stuff map { x => x._2.price(x._1) }