I have a singleton objet with 100 different case classes. For example:
object Foo {
case class Bar1 {
...
}
...
case class Bar100 {
...
}
}
I would like to be able to iterate over each of the case class.
Something like getting all the case classes in a Seq and then being able to map over it.
(map with a polymorphic function for example)
Is it possible using reflection? If yes how? And what are the drawbacks of using reflection here over hard coding a sequence with all the case classes.
Foo.getClass.getDeclaredClasses gives you all the classes declared inside Foo. Because they are case classes, each also defines a companion object (which is also a class), so you'll need to filter them out:
Foo.getClass.getDeclaredClasses.filterNot(_.endsWith("$"))
Reflection is slow, but if you are only going to do it once (no reason to do it more than once, because you'll alway be getting the same result), it's not really an issue.
A bigger problem is that I can't really imagine a "polymorphic function" that would let you do anything useful with this information without some extreme hacking.
Fully parametric polymorphic functions exist in Scala 3. In Scala 2 there are no parametric polymorphic functions (and polymorphic values at all, only polymorphic methods) but there are ad-hoc polymorphic functions implemented normally with Shapeless.
I guess in Shapeless there is a type class (Generic/LabelledGeneric) for iterating case classes extending some sealed trait
case class Bar1() extends MyTrait
//...
case class Bar100() extends MyTrait
Scala how to derivate a type class on a trait
Use the lowest subtype in a typeclass?
Type class instance for case objects defined in sealed trait
Iteration over a sealed trait in Scala?
Getting subclasses of a sealed trait
Can I get a compile-time list of all of the case objects which derive from a sealed parent in Scala?
but not for iterating case classes nested into an object. So probably we'd need a macro (compile-time reflection) anyway, even using Shapeless
import scala.language.experimental.macros
// libraryDependencies += scalaOrganization.value % "scala-reflect" % scalaVersion.value
import scala.reflect.macros.whitebox
// libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.10"
import shapeless.HList
trait GetInnerCaseClasses[A] {
type Out <: HList
}
object GetInnerCaseClasses {
type Aux[A, Out0] = GetInnerCaseClasses[A] { type Out = Out0 }
implicit def mkGetInnerCaseClasses[A, Out <: HList]: Aux[A, Out] =
macro mkGetInnerCaseClassesImpl[A]
def mkGetInnerCaseClassesImpl[A: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
import c.universe._
val A = weakTypeOf[A]
val caseClasses = A.decls.filter(s => s.isClass && s.asClass.isCaseClass)
val hList = caseClasses.foldRight[Tree](tq"_root_.shapeless.HNil")(
(s, hl) => tq"_root_.shapeless.::[$s, $hl]"
)
q"""
new GetInnerCaseClasses[$A] {
override type Out = $hList
}
"""
}
}
// in a different subproject
import shapeless.ops.hlist.{FillWith, Mapper}
import shapeless.{::, HList, HNil, Poly0, Poly1, Typeable, the}
object Foo {
case class Bar1()
// ...
case class Bar100()
}
implicitly[GetInnerCaseClasses.Aux[Foo.type, Bar1 :: Bar2 :: Bar100 :: HNil]] // compiles
val gicc = the[GetInnerCaseClasses[Foo.type]] // "the" is an advanced version of "implicitly" not damaging type refinements
implicitly[gicc.Out =:= (Bar1 :: Bar2 :: Bar100 :: HNil)] // compiles
// I'm just printing names of case classes, you should replace this with your actual iterating logic
object myPoly extends Poly1 {
implicit def cse[A <: Product : Typeable]: Case.Aux[A, Unit] =
at(_ => println(Typeable[A].describe))
}
object nullPoly extends Poly0 {
implicit def cse[A]: Case0[A] = at(null.asInstanceOf[A])
}
def myIterate[A <: Singleton] = new PartiallyAppliedMyIterate[A]
class PartiallyAppliedMyIterate[A <: Singleton] {
def apply[L <: HList]()(implicit
getInnerCaseClasses: GetInnerCaseClasses.Aux[A, L],
mapper: Mapper[myPoly.type, L],
fillWith: FillWith[nullPoly.type, L] // because type class GetInnerCaseClasses works only on type level
): Unit = mapper(fillWith())
}
myIterate[Foo.type]()
// Bar1
// ...
// Bar100
Related
I have a code
case class MyTypeTag[T] ()
def getTypeTags[TT <: Product : TypeTag] = {
val subtypesTags: List[MyTypeTag[Option[_]] = ???
sybtypesTags
}
val res = getTypeTags[(Int, String, Boolean)]
// res = MyTypeTag[Option[Int]] :: MyTypeTag[Option[String]] :: MyTypeTag[Option[Boolean]] :: Nil
so i want to call function getTypeTags passing any tuple type as type parameter and get list of MyTypeTag instances with each type inside tuple wrapped in Option
if in intelliJ i evaluate expression typeof[TT] i see property args with list of my types, but i do not know how to access from code. Or may be some other ways can be apllied.
Thanks in advance
Type parameter T in case class MyTypeTag[T]() must be known at compile time. But it seems you try to define it using runtime reflection. This can't work (unless you define the class at runtime: toolbox.define(q"case class MyTypeTag[T]()") but this would be tricky).
You can try to use compile-time reflection
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def getTypeTags[TT <: Product]: List[MyTypeTag[_ <: Option[_]]] =
macro getTypeTagsImpl[TT]
def getTypeTagsImpl[TT: c.WeakTypeTag](c: blackbox.Context): c.Tree = {
import c.universe._
weakTypeOf[TT].typeArgs.map(t => q"MyTypeTag[Option[$t]]()")
.foldRight[Tree](q"Nil")((t, ts) => q"$t :: $ts")
}
Usage:
getTypeTags[(Int, String, Boolean)] //List(MyTypeTag(), MyTypeTag(), MyTypeTag())
In order to verify that the code works properly let's temporarily modify MyTypeTag
import scala.reflect.runtime.universe.TypeTag
case class MyTypeTag[T]()(implicit val typeTag: TypeTag[T])
val res = getTypeTags[(Int, String, Boolean)]
res(0).typeTag // TypeTag[Option[Int]]
res(1).typeTag // TypeTag[Option[String]]
res(2).typeTag // TypeTag[Option[Boolean]]
You can also use Shapeless instead of macros
import shapeless.ops.hlist.{FillWith, Mapped, ToList}
import shapeless.{Generic, HList, Poly0}
case class MyTypeTag[T]()
def getTypeTags[TT <: Product] = new {
def apply[L <: HList, L1 <: HList]()(implicit
generic: Generic.Aux[TT, L],
mapped: Mapped.Aux[L, λ[X => MyTypeTag[Option[X]]], L1],
fillWith: FillWith[myTypeTagPoly.type, L1],
toList: ToList[L1, MyTypeTag[_ <: Option[_]]]
): List[MyTypeTag[_ <: Option[_]]] =
fillWith().toList
}
object myTypeTagPoly extends Poly0 {
implicit def cse[A]: Case0[MyTypeTag[Option[A]]] = at(MyTypeTag[Option[A]]())
}
getTypeTags[(Int, String, Boolean)]() // List(MyTypeTag(), MyTypeTag(), MyTypeTag())
If you make MyTypeTag covariant (MyTypeTag[+T]) then List[MyTypeTag[_ <: Option[_]]] can be replaced with List[MyTypeTag[Option[_]]].
You can't distinguish between an instance of MyTypeTag[Int] and an instance of MyTypeTag[String] at runtime (to check this for yourself, try
val l = List(MyTypeTag[Option[Int]](), MyTypeTag[Option[String]](), MyTypeTag[Option[Boolean]]())
and see what that gives you, and what you can and can't do with it), so the answer to the question as you ask it is
def getTypeTags[TT <: Product](implicit tt: TypeTag[TT]): List[MyTypeTag[_]] = {
tt.tpe.typeParams.map(_ => MyTypeTag[Option[_]]())
}
You can get the type parameters with tt.tpe.typeParams, but since that's a runtime value, you can't recover that as a compile-time type T for MyTypeTag[T] since it doesn't exist at compile-time yet.
Maybe you can leverage shapeless to do whatever it is you want to do, it has ways to abstract over tuples. See https://underscore.io/books/shapeless-guide/
This is a follow-up of one of my previous questions:
In scala shapeless, is it possible to use literal type as a generic type parameter?
I'm trying to write scala code for vector multiplication, while using shapeless to make the compiler aware of the dimension of each vector:
trait IntTypeMagnet[W <: Witness.Lt[Int]] extends Serializable {
def witness: W
}
object IntTypeMagnet {
case class Impl[W <: Witness.Lt[Int]](witness: W) extends IntTypeMagnet[W] {}
implicit def fromInt[W <: Int](v: W): Impl[Lt[W]] = Impl(Witness(v))
implicit def fromWitness[W <: Witness.Lt[Int]](witness: W): Impl[W] = Impl(witness)
}
trait Axis extends Serializable
case object UnknownAxis extends Axis
trait KnownAxis[W <: Witness.Lt[Int]] extends Axis {
def n: Int
def ++(that: KnownAxis[W]): Unit = {}
}
Ideally the implicit fromInt can deduce v.narrow (Witness can only work on final, singleton type), yet it doesn't behave as so:
object Attempt1 {
case class KN[W <: Witness.Lt[Int]](magnet: IntTypeMagnet[W]) extends KnownAxis[W] {
val n = magnet.witness.value
}
// looking good, works as intended
KN(1) ++ KN(1)
KN(Witness(1)) ++ KN(2)
KN(Witness(1)) ++ KN(Witness(2))
val v1_simple: KN[Lt[Int]] = KN(1)
KN(1) ++ KN(2) // should break
KN(1) ++ KN(Witness(2)) // should break
}
The last 2 lines doesn't cause any compilation error, which contradicts my hypothesis. Is there a way to change this behaviour such that KN created using fromInt can find the correct type? I'm using scala-2.12 so the singleton type of shapeless seems to be the only option.
I am trying to find a way, in shapeless, to prove that a given product type extends no sealed trait, and hence does not belong to any coproduct. Given the following sealed trait hierarchy:
sealed trait Foo
case class Bar(a: Char) extends Foo
case class Baz(b: Int) extends Foo
I know I can use shapeless.ops.coproduct.Basis to prove that a given choice, or subsequence of choices, belongs to a co-product. Eg:
import shapeless._
type L = Bar :+: Baz :+: CNil
implicitly[ops.coproduct.Basis[L, Bar :+: CNil]]
What I am after now, is an operation to get the coproduct from a choice. Eg. given Bar or Baz, I would like to get back L, or alternatively, the type of the sealed base trait Foo.
Is this something that shapeless can do? alternatively, is it possible to do it with macros?
Update:
I ended up writing a fairly involved implicit macro...
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
trait SealedBaseTraitOf[A] {
type Repr
}
object SealedBaseTraitOf {
def materializeImpl[A](c: whitebox.Context)(implicit tag: c.WeakTypeTag[A]): c.Expr[SealedBaseTraitOf[A]] = {
import c.universe._
val a = weakTypeOf[A].typeSymbol
val baseClasses = a.asClass.baseClasses
val maybeBaseTrait =
baseClasses
.find(t => t.asClass.isTrait && t.asClass.isSealed)
.map(_.asType.name)
val repr = maybeBaseTrait.map(t => tq"$t").getOrElse(tq"Nothing")
println(s"Got a repr: $repr")
c.Expr[SealedBaseTraitOf[A]](q"""
new SealedBaseTraitOf[$a] {
type Repr = $repr
}
""")
}
implicit def materialize[A]:SealedBaseTraitOf[A] = macro materializeImpl[A]
}
Putting all together, the macro can be used as follows:
case class ExtendsNothing(a: Int)
sealed trait X
case class ExtendsX(b: Char) extends X
import shapeless._
val bt1 = the[SealedBaseTraitOf[ExtendsNothing]]
implicitly[bt1.Repr =:= Nothing]
val bt2 = the[SealedBaseTraitOf[ExtendsX]]
implicitly[bt2.Repr =:= X]
val coprodX = Generic[X]
val coprodBt2 = Generic[bt2.Repr]
implicitly[coprodX.Repr =:= coprodBt2.Repr]
implicitly[ops.coproduct.Basis[coprodBt2.Repr, ExtendsX :+: CNil]]
While getting close to a solution, I am still hoping to find something a bit less involved, possibly which doesn't involve using macros.
I have the simplified situation:
abstract sealed trait Top
class A[T] extends Top
class B[T] extends Top
class Typeclass[T]
implicit def a[T] = new Typeclass[A[T]]
implicit def b[T] = new Typeclass[B[T]]
Now I have a Map[String, Top] and want to use an operation on all values in the map that require the presence of an instance of Typeclass to be available in the context. This will not compile as the concrete types of the values in the map are not visible from its type and I can therefore not set a context bound for them.
Is there a way to tell the compiler that in fact there will always be an instance available? In this example this is given as there are implicit functions to generate those instances for every concrete subtype of Top.
Or is the only solution to use a HList and recurse over its type requiring all the instances to be in context?
I recommend using some variation on this adaptation of Oleg's Existentials as universals in this sort of situation ... pack the the type class instance along with the value it's the instance for,
abstract sealed trait Top
class A[T] extends Top
class B[T] extends Top
class Typeclass[T]
implicit def a[T] = new Typeclass[A[T]]
implicit def b[T] = new Typeclass[B[T]]
trait Pack {
type T <: Top
val top: T
implicit val tc: Typeclass[T]
}
object Pack {
def apply[T0 <: Top](t0: T0)(implicit tc0: Typeclass[T0]): Pack =
new Pack { type T = T0 ; val top = t0 ; val tc = tc0 }
}
val m = Map("a" -> Pack(new A[Int]), "b" -> Pack(new B[Double]))
def foo[T: Typeclass](t: T): Unit = ()
def bar(m: Map[String, Pack], k: String): Unit =
m.get(k).map { pack =>
import pack._ // imports T, top and implicit tc
foo(top) // instance available for call of foo
}
bar(m, "a")
As discussed in comment it would be more convenient to have the typeclass defined on Top, and it might be done with pattern matching.
supposing part of the definition of the typeclass is
def f[T](t: T): FResult[T],
and you have the corresponding implentations
def fOnA[T](t: A[T]): FResult[A[T]] = ...
def fOnB[T](t: B[T]): FResult[B[T]] = ...
Then you can define
def fOnT(t: Top) : FResult[Top] = t match {
case a: A[_] => fOnA(a)
// provided an FResult[A[T]] is an FResult[Top],
// or some conversion is possible
case b: B[_] => fOnB(b)
}
If is both legal and safe to call a generic method, such as fOnA[T] with an existential (a matching A[_])
However, it might be difficult to convince the compiler that the parameter you pass to f or the result you get are ok, given the reduced information of the existential. If so, please post the signatures you need.
Let's say I have a coproduct (a sealed trait) such as
sealed trait Traity
case object Foo extends Traity
case class Bar() extends Traity
case class Baz() extends Traity
Using shapeless, I can apply polymorphic functions to specific instances but what I'd like to do is to apply a zero-parameter (no-instance) polymorphic function to all the products (i.e. case classes and case objects). I have no idea what the syntax would look like, but something conceptually like:
object mypoly extends Poly1 {
implicit def traity[T <: Traity] = when[T]( getClass[T].toString )
}
iterate[Traity](mypoly) // gives List("Foo", "Bar", "Baz")
would suit my purposes.
For the example use case in your question, this is actually very straightforward:
import shapeless._
class NameHelper[A] {
def apply[C <: Coproduct, K <: HList]()(implicit
gen: LabelledGeneric.Aux[A, C],
keys: ops.union.Keys.Aux[C, K],
toSet: ops.hlist.ToTraversable.Aux[K, Set, Symbol]
): Set[String] = toSet(keys()).map(_.name)
}
def names[A] = new NameHelper[A]
And then:
scala> names[Traity]()
res0: Set[String] = Set(Bar, Baz, Foo)
(I'm using a Set since the order you're getting is just alphabetical—it's not currently possible to enumerate the constructors in declaration order, although I'd personally prefer that.)
If you'd like a more generic answer, an adaptation of the code in the question I linked above shouldn't be too bad—I'd be happy to add it here later.