Scala case classes and recursive reflection - scala

Given 2 Scala case classes
case class Bar(x: Int)
case class Foo(b: Bar, z: Double)
I have a piece of code that prints the types of Foo fields using reflection:
import scala.reflect.runtime.universe._
def f[T: TypeTag] = typeOf[T].members.filter(!_.isMethod)
and I call it like f[Foo] and f[Bar]. Calling the former returns a List[Type] as [Bar, Double].
How can I call f on the first element of the list? Equivalently, how can I print types recursively when Foo has a custom class Bar? Equivalently how can I get from Bar as Type a Bar.type?
Many thanks

You don't actually need the type variable T in f. You can define it like this (as Dima suggested in the comments):
def f(t: Type) =
t.members.filter(!_.isMethod).map(_.typeSignature)
To use this to recursively print a type:
def printTypesRecursive(t: Type, prefix: String = ""): Unit = {
println(prefix + t)
f(t).foreach(printTypesRecursive(_, prefix + " "))
}
printTypesRecursive(typeOf[Foo])
Output:
Foo
Double
Bar
Int

Equivalently how can I get from Bar as Type a Bar.type?
Bar.type is the type of companion object
Class companion object vs. case class itself
I need something like f[f[Foo].head]
I guess you have here some confusion between compile-time and runtime
Runtime vs. Compile time
You can call
def f[T: TypeTag] = typeOf[T].members.filter(!_.isMethod)
f[Foo]
//Scope{
// private[this] val z: <?>;
// private[this] val b: <?>
//}
if you know type T statically i.e. at compile time (earlier).
You can call
def f_dyn(tpe: Type) = tpe.members.filter(!_.isMethod)
f_dyn(typeOf[Foo])
//Scope{
// private[this] val z: <?>;
// private[this] val b: <?>
//}
if you know type tpe dynamically i.e. at runtime (later).
You can express f via f_dyn
def f[T: TypeTag] = f_dyn(typeOf[T])
def f_dyn(tpe: Type) = tpe.members.filter(!_.isMethod)
If you want to iterate the method (apply it recursively) then it should return something like it accepts, i.e. now this is types rather than symbols, so you need to add somewhere something like .typeSignature, .asMethod.returnType, .asType.toType. Also maybe now you're more interested in .decls rather than .members since you are not looking for inherited members. Also .decls returns field symbols in correct order on contrary to .members. Finally let it be better List[...] rather than raw Scope (.toList)
def f[T: TypeTag]: List[Type] = f_dyn(typeOf[T])
def f_dyn(tpe: Type): List[Type] =
tpe.decls.filter(!_.isMethod).map(_.typeSignature).toList
f_dyn(f[Foo].head) // List(Int)
f_dyn(f_dyn(typeOf[Foo]).head) // List(Int)
You can iterate f_dyn
f_dyn(typeOf[Foo]) // List(Bar, Double)
f_dyn(typeOf[Foo]).map(f_dyn) // List(List(Int), List())
f_dyn(typeOf[Foo]).map(f_dyn).map(_.map(f_dyn)) // List(List(List()), List())
If you really want to iterate f rather than f_dyn then the complication is that you can call f[T] for the second time only on a statically known type T but you have the type that is the result of the first call only at runtime, you don't have it at compile time. In principle you can use runtime compilation (creating new compile time inside runtime) although this can work slower than ordinary reflection and doesn't seem needed now
import scala.reflect.runtime.{currentMirror => rm}
import scala.tools.reflect.ToolBox // libraryDependencies += scalaOrganization.value % "scala-compiler" % scalaVersion.value
val tb = rm.mkToolBox()
// suppose f is defined in object App
tb.eval(q"App.f[${f[Foo].head}]") // List(Int)
tb.eval(q"""
import App._
f[${f[Foo].head}]
""")
// List(Int)
Now all the classes Foo, Bar... are defined at compile time so it would make sense to use compile-time reflection (macros) rather than runtime reflection
Getting Case Class definition which points to another Case Class
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def f[T]: List[String] = macro Macros.f_impl[T]
def f1[T]: List[List[String]] = macro Macros.f1_impl[T]
def f2[T]: List[List[List[String]]] = macro Macros.f2_impl[T]
class Macros(val c: blackbox.Context) {
import c.universe._
def f_dyn(tpe: Type): List[Type] =
tpe.decls.filter(!_.isMethod).map(_.typeSignature).toList
val ListObj = q"_root_.scala.List"
val ListT = tq"_root_.scala.List"
val StringT = tq"_root_.scala.Predef.String"
def f_impl[T: WeakTypeTag]: Tree = {
val types: List[Type] = f_dyn(weakTypeOf[T])
val typeStrings: List[String] = types.map(_.toString)
q"$ListObj.apply[$StringT](..$typeStrings)"
}
def f1_impl[T: WeakTypeTag]: Tree = {
val types: List[List[Type]] = f_dyn(weakTypeOf[T]).map(f_dyn)
val typeStrings: List[List[String]] = types.map(_.map(_.toString))
q"$ListObj.apply[$ListT[$StringT]](..$typeStrings)"
}
def f2_impl[T: WeakTypeTag]: Tree = {
val types: List[List[List[Type]]] =
f_dyn(weakTypeOf[T]).map(f_dyn).map(_.map(f_dyn))
val typeStrings: List[List[List[String]]] = types.map(_.map(_.map(_.toString)))
q"$ListObj.apply[$ListT[$ListT[$StringT]]](..$typeStrings)"
}
}
// in a different subproject
f[Foo]
//scalac: _root_.scala.List.apply[_root_.scala.Predef.String]("Bar", "Double")
f1[Foo]
//scalac: _root_.scala.List.apply[_root_.scala.List[_root_.scala.Predef.String]](scala.collection.immutable.List("Int"), scala.collection.immutable.List())
f2[Foo]
//scalac: _root_.scala.List.apply[_root_.scala.List[_root_.scala.List[_root_.scala.Predef.String]]](scala.collection.immutable.List(scala.collection.immutable.List()), scala.collection.immutable.List())
The runtime of macros (when they are expanded) is the compile time of main code.
Do macros support annotations too? like can I access my case class annotations with macros? with runtime reflection , i would do symbolOf[Foo].asClass.annotations
Yes, surely.
def foo[T]: Unit = macro fooImpl[T]
def fooImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Tree = {
import c.universe._
println(symbolOf[T].asClass.annotations)
q"()"
}
class myAnnot extends StaticAnnotation
#myAnnot
case class Foo(b: Bar, z: Double)
symbolOf[Foo].asClass.annotations // at runtime: List(myAnnot)
foo[Foo]
// at compile time with scalacOptions += "-Ymacro-debug-lite":
// scalac: List(myAnnot)
One more option to perform compile-time calculations is to use one of libraries encapsulating work with macros e.g. Shapeless
// libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.10"
import shapeless.{::, DepFn0, DepFn1, HList, HNil, Generic, Poly0, Poly1, Typeable, poly}
trait DeepGeneric[T <: Product] {
type Repr <: HList
def to(t: T): Repr
def from(r: Repr): T
}
object DeepGeneric {
type Aux[T <: Product, Repr0 <: HList] = DeepGeneric[T] {type Repr = Repr0}
def instance[T <: Product, Repr0 <: HList](f: T => Repr0, g: Repr0 => T): Aux[T, Repr0] = new DeepGeneric[T] {
override type Repr = Repr0
override def to(t: T): Repr = f(t)
override def from(r: Repr): T = g(r)
}
implicit def deepGeneric[A <: Product, L <: HList, L1 <: HList](implicit
generic: Generic.Aux[A, L],
hListDeepGeneric: HListDeepGeneric.Aux[L, L1]
): Aux[A, L1] = instance(a => hListDeepGeneric.to(generic.to(a)), l1 => generic.from(hListDeepGeneric.from(l1)))
}
trait HListDeepGeneric[T <: HList] {
type Repr <: HList
def to(t: T): Repr
def from(r: Repr): T
}
trait LowPriorityHListDeepGeneric {
type Aux[T <: HList, Repr0 <: HList] = HListDeepGeneric[T] {type Repr = Repr0}
def instance[T <: HList, Repr0 <: HList](f: T => Repr0, g: Repr0 => T): Aux[T, Repr0] = new HListDeepGeneric[T] {
override type Repr = Repr0
override def to(t: T): Repr = f(t)
override def from(r: Repr): T = g(r)
}
implicit def headNotCaseClass[H, T <: HList, T_hListDeepGen <: HList](implicit
tailHListDeepGeneric: HListDeepGeneric.Aux[T, T_hListDeepGen]
): Aux[H :: T, H :: T_hListDeepGen] = instance({
case h :: t => h :: tailHListDeepGeneric.to(t)
}, {
case h :: t => h :: tailHListDeepGeneric.from(t)
})
}
object HListDeepGeneric extends LowPriorityHListDeepGeneric {
implicit val hNil: Aux[HNil, HNil] = instance(identity, identity)
implicit def headCaseClass[H <: Product, T <: HList, H_deepGen <: HList, T_hListDeepGen <: HList](implicit
headDeepGeneric: DeepGeneric.Aux[H, H_deepGen],
tailHListDeepGeneric: HListDeepGeneric.Aux[T, T_hListDeepGen]
): Aux[H :: T, H_deepGen :: T_hListDeepGen] = instance({
case h :: t => headDeepGeneric.to(h) :: tailHListDeepGeneric.to(t)
}, {
case h :: t => headDeepGeneric.from(h) :: tailHListDeepGeneric.from(t)
})
}
trait DeepMapper[P <: Poly1, In <: HList] extends DepFn1[In] {
type Out <: HList
}
trait LowPriorityDeepMapper {
type Aux[P <: Poly1, In <: HList, Out0 <: HList] = DeepMapper[P, In] {type Out = Out0}
def instance[P <: Poly1, In <: HList, Out0 <: HList](f: In => Out0): Aux[P, In, Out0] = new DeepMapper[P, In] {
override type Out = Out0
override def apply(t: In): Out = f(t)
}
implicit def headNotHList[P <: Poly1, H, T <: HList](implicit
headCase: poly.Case1[P, H],
tailDeepMapper: DeepMapper[P, T]
): Aux[P, H :: T, headCase.Result :: tailDeepMapper.Out] =
instance(l => headCase(l.head) :: tailDeepMapper(l.tail))
}
object DeepMapper extends LowPriorityDeepMapper {
implicit def hNil[P <: Poly1]: Aux[P, HNil, HNil] = instance(_ => HNil)
// implicit def headHList[P <: Poly1, H <: HList, H_deepMap <: HList, T <: HList](implicit
// headDeepMapper: DeepMapper.Aux[P, H, H_deepMap],
// headCase: poly.Case1[P, H_deepMap], // apply poly one more time
// tailDeepMapper: DeepMapper[P, T]
// ): Aux[P, H :: T, headCase.Result :: tailDeepMapper.Out] =
// instance(l => headCase(headDeepMapper(l.head)) :: tailDeepMapper(l.tail))
implicit def headHList[P <: Poly1, H <: HList, T <: HList](implicit
headDeepMapper: DeepMapper[P, H], // don't apply poly one more time
tailDeepMapper: DeepMapper[P, T]
): Aux[P, H :: T, headDeepMapper.Out :: tailDeepMapper.Out] =
instance(l => headDeepMapper(l.head) :: tailDeepMapper(l.tail))
}
trait DeepFillWith[P <: Poly0, L <: HList] extends DepFn0 {
type Out = L
}
trait LowPriorityDeepFillWith {
def apply[P <: Poly0, L <: HList](implicit deepFillWith: DeepFillWith[P, L]): DeepFillWith[P, L] = deepFillWith
def instance[P <: Poly0, L <: HList](f: => L): DeepFillWith[P, L] = new DeepFillWith[P, L] {
override def apply(): L = f
}
implicit def headNotHList[P <: Poly0, H, T <: HList](implicit
headCase: poly.Case0.Aux[P, H],
tailDeepFillWith: DeepFillWith[P, T]
): DeepFillWith[P, H :: T] =
instance(headCase() :: tailDeepFillWith())
}
object DeepFillWith extends LowPriorityDeepFillWith {
implicit def hNil[P <: Poly0]: DeepFillWith[P, HNil] = instance(HNil)
implicit def headHList[P <: Poly0, H <: HList, T <: HList](implicit
headDeepFillWith: DeepFillWith[P, H],
tailDeepFillWith: DeepFillWith[P, T]
): DeepFillWith[P, H :: T] =
instance(headDeepFillWith() :: tailDeepFillWith())
}
// needed if DeepMapper "applies poly one more time",
// e.g. for field NAMES and types (via DeepLabelledGeneric), not just types (via DeepGeneric)
// trait LowPriorityTypeablePoly extends Poly1 {
// implicit def notHListCase[V](implicit typeable: Typeable[V]): Case.Aux[V, String] =
// at(_ => typeable.describe)
// }
//
// object typeablePoly extends LowPriorityTypeablePoly {
// implicit def hListCase[V <: HList]: Case.Aux[V, V] = at(identity)
// }
object typeablePoly extends Poly1 {
implicit def cse[A](implicit typeable: Typeable[A]): Case.Aux[A, String] =
at(_ => typeable.describe)
}
object nullPoly extends Poly0 {
implicit def cse[A]: Case0[A] = at(null.asInstanceOf[A])
}
def classFieldTypes[T <: Product] = new PartiallyApplied[T]
class PartiallyApplied[T <: Product] {
def apply[L <: HList]()(implicit
deepGeneric: DeepGeneric.Aux[T, L],
deepFillWith: DeepFillWith[nullPoly.type, L],
deepMapper: DeepMapper[typeablePoly.type, L],
): deepMapper.Out = deepMapper(deepFillWith())
}
classFieldTypes[Bar]() // Int :: HNil
classFieldTypes[Foo]() // (Int :: HNil) :: Double :: HNil
Generic/LabelledGeneric/DeepGeneric, Mapper/DeepMapper, FillWith/DeepFillWith, Typeable are type classes.
lets say for each Type I want the code to behave differently, if Double do x, if Int do y.
You can use types comparisons t =:= typeOf[Double], t <:< typeOf[Double] if you use runtime/compile-time reflection or you can keep using type classes and polymorphic functions
trait MyTypeclass[T] {
def apply(): Unit
}
object MyTypeclass {
implicit val double: MyTypeclass[Double] = () => println("do x")
implicit val int: MyTypeclass[Int] = () => println("do y")
implicit def caseClass[T <: Product, L <: HList](implicit
deepGeneric: DeepGeneric.Aux[T, L],
deepFillWith: DeepFillWith[nullPoly.type, L],
deepMapper: DeepMapper[myPoly.type, L]
): MyTypeclass[T] = () => deepMapper(deepFillWith())
}
object myPoly extends Poly1 {
implicit def cse[T: MyTypeclass]: Case.Aux[T, Unit] = at(_ => foo)
}
def foo[T](implicit tc: MyTypeclass[T]): Unit = tc()
foo[Int]
// do y
foo[Double]
// do x
foo[Foo]
// do y
// do x
foo[Bar]
// do y
Shapeless is also capable of handling annotations
import shapeless.Annotation
implicitly[Annotation[myAnnot, Foo]].apply() // myAnnot#1a3869f4

Related

Getting Case Class definition which points to another Case Class

I am looking at getting case class definitions.
From SO I gleaned this practice as per Get field names list from case class, the answer using reflection by Dia Kharrat.
Some experimenting in which I have a case class referring to another case class, nested. Can we get the metadata expanded easily in some way?
import scala.collection.mutable.ArrayBuffer
case class MyChgClass(b: Option[String], c: Option[String], d: Option[String])
case class MyFullClass(k: Int, b: String, c: String, d: String)
case class MyEndClass(id: Int, after: MyFullClass)
def classAccessors[T: TypeTag]: List[MethodSymbol] = typeOf[T].members.collect {
case m: MethodSymbol if m.isCaseAccessor => m
}.toList
val z1 = classAccessors[MyChgClass]
val z2 = classAccessors[MyFullClass]
val z3 = classAccessors[MyEndClass]
returns:
z1: List[reflect.runtime.universe.MethodSymbol] = List(value d, value c, value b)
z2: List[reflect.runtime.universe.MethodSymbol] = List(value d, value c, value b, value k)
z3: List[reflect.runtime.universe.MethodSymbol] = List(value after, value id)
So:
Looking to expand the case class MyEndClass.
The option aspect appears not not been supplied. Possible?
The option aspect appears not not been supplied. Possible?
Are you looking for .name and .typeSignature?
val z1 = classAccessors[MyChgClass]
val z2 = classAccessors[MyFullClass]
val z3 = classAccessors[MyEndClass]
z1.map(_.name) // List(d, c, b)
z1.map(_.typeSignature) // List(Option[String], Option[String], Option[String])
z2.map(_.name) // List(d, c, b, k)
z2.map(_.typeSignature) // List(String, String, String, Int)
z3.map(_.name) // List(after, id)
z3.map(_.typeSignature) // List(MyFullClass, Int)
If your classes are known at compile time it would make sense to use compile-time reflection i.e. macros rather than runtime reflection
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def classAccessors[T]: List[(String, String)] = macro classAccessorsImpl[T]
def classAccessorsImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Tree = {
import c.universe._
val pairs = weakTypeOf[T].members.collect {
case m: MethodSymbol if m.isCaseAccessor => m
}.map(m => (m.name.toString, m.typeSignature.toString))
q"List.apply[(String, String)](..$pairs)"
}
// in a different subproject
classAccessors[MyChgClass] // List((d,Option[String]), (c,Option[String]), (b,Option[String]))
classAccessors[MyFullClass] // List((d,String), (c,String), (b,String), (k,Int))
classAccessors[MyEndClass] // List((after,MyFullClass), (id,Int))
Even better would be to use one of libraries encapsulating those macros into some type classes. In Shapeless the type class giving access to names and types of case-class fields is LabelledGeneric
// libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.10"
import shapeless.labelled.FieldType
import shapeless.ops.hlist.{FillWith, Mapper, ToList}
import shapeless.{HList, LabelledGeneric, Poly0, Poly1, Typeable, Witness}
object fieldNamesAndTypesPoly extends Poly1 {
implicit def cse[K <: Symbol, V](implicit
witness: Witness.Aux[K],
typeable: Typeable[V]
): Case.Aux[FieldType[K, V], (String, String)] =
at(_ => (witness.value.name, typeable.describe))
}
object nullPoly extends Poly0 {
implicit def cse[A]: Case0[A] = at(null.asInstanceOf[A])
}
def classAccessors[T] = new PartiallyApplied[T]
class PartiallyApplied[T] {
def apply[L <: HList, L1 <: HList]()(implicit
labelledGeneric: LabelledGeneric.Aux[T, L],
fillWith: FillWith[nullPoly.type, L],
mapper: Mapper.Aux[fieldNamesAndTypesPoly.type, L, L1],
toList: ToList[L1, (String, String)]
): List[(String, String)] = toList(mapper(fillWith()))
}
classAccessors[MyChgClass]() // List((b,Option[String]), (c,Option[String]), (d,Option[String]))
classAccessors[MyFullClass]() // List((k,Int), (b,String), (c,String), (d,String))
classAccessors[MyEndClass]() // List((id,Int), (after,MyFullClass))
Looking to expand the case class MyEndClass.
You can try deep versions of the type classes LabelledGeneric, Mapper etc.
import shapeless.labelled.{FieldType, field}
import shapeless.{::, DepFn0, DepFn1, HList, HNil, LabelledGeneric, Poly0, Poly1, Typeable, Witness, poly}
trait DeepLabelledGeneric[T <: Product] {
type Repr <: HList
def to(t: T): Repr
def from(r: Repr): T
}
object DeepLabelledGeneric {
type Aux[T <: Product, Repr0 <: HList] = DeepLabelledGeneric[T] {type Repr = Repr0}
def instance[T <: Product, Repr0 <: HList](f: T => Repr0, g: Repr0 => T): Aux[T, Repr0] = new DeepLabelledGeneric[T] {
override type Repr = Repr0
override def to(t: T): Repr = f(t)
override def from(r: Repr): T = g(r)
}
implicit def deepGeneric[A <: Product, L <: HList, L1 <: HList](implicit
labelledGeneric: LabelledGeneric.Aux[A, L],
hListDeepLabelledGeneric: HListDeepLabelledGeneric.Aux[L, L1]
): Aux[A, L1] = instance(a => hListDeepLabelledGeneric.to(labelledGeneric.to(a)), l1 => labelledGeneric.from(hListDeepLabelledGeneric.from(l1)))
}
trait HListDeepLabelledGeneric[T <: HList] {
type Repr <: HList
def to(t: T): Repr
def from(r: Repr): T
}
trait LowPriorityHListDeepLabelledGeneric {
type Aux[T <: HList, Repr0 <: HList] = HListDeepLabelledGeneric[T] {type Repr = Repr0}
def instance[T <: HList, Repr0 <: HList](f: T => Repr0, g: Repr0 => T): Aux[T, Repr0] = new HListDeepLabelledGeneric[T] {
override type Repr = Repr0
override def to(t: T): Repr = f(t)
override def from(r: Repr): T = g(r)
}
implicit def headNotCaseClass[H, T <: HList, T_hListDeepLGen <: HList](implicit
tailHListDeepLabelledGeneric: HListDeepLabelledGeneric.Aux[T, T_hListDeepLGen]
): Aux[H :: T, H :: T_hListDeepLGen] = instance({
case h :: t => h :: tailHListDeepLabelledGeneric.to(t)
}, {
case h :: t => h :: tailHListDeepLabelledGeneric.from(t)
})
}
object HListDeepLabelledGeneric extends LowPriorityHListDeepLabelledGeneric {
implicit val hNil: Aux[HNil, HNil] = instance(identity, identity)
implicit def headCaseClass[K <: Symbol, H <: Product, T <: HList, H_deepLGen <: HList, T_hListDeepLGen <: HList](implicit
headDeepLabelledGeneric: DeepLabelledGeneric.Aux[H, H_deepLGen],
tailHListDeepLabelledGeneric: HListDeepLabelledGeneric.Aux[T, T_hListDeepLGen]
): Aux[FieldType[K, H] :: T, FieldType[K, H_deepLGen] :: T_hListDeepLGen] = instance({
case h :: t => field[K](headDeepLabelledGeneric.to(h)) :: tailHListDeepLabelledGeneric.to(t)
}, {
case h :: t => field[K](headDeepLabelledGeneric.from(h)) :: tailHListDeepLabelledGeneric.from(t)
})
}
trait DeepMapper[P <: Poly1, In <: HList] extends DepFn1[In] {
type Out <: HList
}
trait LowPriorityDeepMapper {
def apply[P <: Poly1, L <: HList](implicit deepMapper: DeepMapper[P, L]): Aux[P, L, deepMapper.Out] = deepMapper
type Aux[P <: Poly1, In <: HList, Out0 <: HList] = DeepMapper[P, In] {type Out = Out0}
def instance[P <: Poly1, In <: HList, Out0 <: HList](f: In => Out0): Aux[P, In, Out0] = new DeepMapper[P, In] {
override type Out = Out0
override def apply(t: In): Out = f(t)
}
implicit def headNotHList[P <: Poly1, H, T <: HList](implicit
headCase: poly.Case1[P, H],
tailDeepMapper: DeepMapper[P, T]
): Aux[P, H :: T, headCase.Result :: tailDeepMapper.Out] =
instance(l => headCase(l.head) :: tailDeepMapper(l.tail))
}
object DeepMapper extends LowPriorityDeepMapper {
implicit def hNil[P <: Poly1]: Aux[P, HNil, HNil] = instance(_ => HNil)
implicit def headHList[P <: Poly1, K <: Symbol, H <: HList, H_deepMap <: HList, T <: HList](implicit
headDeepMapper: DeepMapper.Aux[P, H, H_deepMap],
headCase: poly.Case1[P, FieldType[K, H_deepMap]], // apply poly one more time
tailDeepMapper: DeepMapper[P, T]
): Aux[P, FieldType[K, H] :: T, headCase.Result :: tailDeepMapper.Out] =
instance(l => headCase(field[K](headDeepMapper(l.head))) :: tailDeepMapper(l.tail))
}
trait DeepFillWith[P <: Poly0, L <: HList] extends DepFn0 {
type Out = L
}
trait LowPriorityDeepFillWith {
def apply[P <: Poly0, L <: HList](implicit deepFillWith: DeepFillWith[P, L]): DeepFillWith[P, L] = deepFillWith
def instance[P <: Poly0, L <: HList](f: => L): DeepFillWith[P, L] = new DeepFillWith[P, L] {
override def apply(): L = f
}
implicit def headNotHList[P <: Poly0, H, T <: HList](implicit
headCase: poly.Case0.Aux[P, H],
tailDeepFillWith: DeepFillWith[P, T]
): DeepFillWith[P, H :: T] =
instance(headCase() :: tailDeepFillWith())
}
object DeepFillWith extends LowPriorityDeepFillWith {
implicit def hNil[P <: Poly0]: DeepFillWith[P, HNil] = instance(HNil)
implicit def headHList[P <: Poly0, K <: Symbol, H <: HList, T <: HList](implicit
headDeepFillWith: DeepFillWith[P, H],
tailDeepFillWith: DeepFillWith[P, T]
): DeepFillWith[P, FieldType[K, H] :: T] =
instance(field[K](headDeepFillWith()) :: tailDeepFillWith())
}
trait LowPriorityFieldNamesAndTypesPoly extends Poly1 {
implicit def notHListCase[K <: Symbol, V](implicit
witness: Witness.Aux[K],
typeable: Typeable[V]
): Case.Aux[FieldType[K, V], (String, String)] =
at(_ => (witness.value.name, typeable.describe))
}
object fieldNamesAndTypesPoly extends LowPriorityFieldNamesAndTypesPoly {
implicit def hListCase[K <: Symbol, V <: HList](implicit
witness: Witness.Aux[K],
): Case.Aux[FieldType[K, V], (String, V)] =
at(v => (witness.value.name, v)) // for DeepMapper "applying this poly one more time"
}
object nullPoly extends Poly0 {
implicit def cse[A]: Case0[A] = at(null.asInstanceOf[A])
}
def classAccessors[T <: Product] = new PartiallyApplied[T]
class PartiallyApplied[T <: Product] {
def apply[L <: HList]()(implicit
deepLabelledGeneric: DeepLabelledGeneric.Aux[T, L],
deepFillWith: DeepFillWith[nullPoly.type, L],
deepMapper: DeepMapper[fieldNamesAndTypesPoly.type, L],
): deepMapper.Out = deepMapper(deepFillWith())
}
classAccessors[MyChgClass]() // (b,Option[String]) :: (c,Option[String]) :: (d,Option[String]) :: HNil
classAccessors[MyFullClass]() // (k,Int) :: (b,String) :: (c,String) :: (d,String) :: HNil
classAccessors[MyEndClass]() // (id,Int) :: (after,(k,Int) :: (b,String) :: (c,String) :: (d,String) :: HNil) :: HNil
Deriving nested shapeless lenses using only a type
Weird behavior trying to convert case classes to heterogeneous lists recursively with Shapeless
https://github.com/milessabin/shapeless/blob/main/examples/src/main/scala/shapeless/examples/deephlister.scala
Converting nested case classes to nested Maps using Shapeless
Automatically convert a case class to an extensible record in shapeless?

Poly fold return type not inferred correctly

I am trying to create a poly function that folds over a tuple of Foos:
case class Foo[A](a: A)
object extractFold extends Poly2 {
implicit def default[A, As <: HList]: Case.Aux[Foo[A], Foo[As], Foo[A :: As]] = {
???
}
}
def extract[In, A <: HList, B <: HList](keys: In)
(implicit
gen: Generic.Aux[In, A],
folder: RightFolder.Aux[A, Foo[HNil], extractFold.type, Foo[B]],
tupler: Tupler[B])
: Foo[tupler.Out] = {
???
}
val result = extract((Foo(1), Foo("a")))
The function works at runtime, but the compiler inferred result type is always Foo[Unit] which is not right - in this example it should be Foo[(Int, String)]
Why do you think that
the compiler inferred result type is always Foo[Unit]
?
The following code
import shapeless.ops.hlist.{RightFolder, Tupler}
import shapeless.{::, Generic, HList, HNil, Poly2}
import scala.reflect.runtime.universe.{typeOf, Type, TypeTag}
object App {
def getType[T: TypeTag](t: T): Type = typeOf[T]
case class Foo[A](a: A)
object extractFold extends Poly2 {
implicit def default[A, As <: HList]: Case.Aux[Foo[A], Foo[As], Foo[A :: As]] =
at { case (Foo(a), Foo(as)) => Foo(a :: as) }
}
def extract[In, A <: HList, B <: HList](keys: In)(implicit
gen: Generic.Aux[In, A],
folder: RightFolder.Aux[A, Foo[HNil], extractFold.type, Foo[B]],
tupler: Tupler[B]
): Foo[tupler.Out] =
Foo(tupler(folder(gen.to(keys), Foo(HNil)).a))
val result = extract((Foo(1), Foo("a")))
def main(args: Array[String]): Unit = {
println(
getType(result)
)
}
}
prints
App.Foo[(Int, java.lang.String)]
Moreover, if you change the line
val result: Foo[Unit] = extract((Foo(1), Foo("a")))
the code doesn't compile.
By the way, the same can be done with
import shapeless.PolyDefns.~>
import shapeless.ops.hlist.{Comapped, NatTRel, Tupler}
import shapeless.{Generic, HList, Id}
object App {
case class Foo[A](a: A)
def extract[In, A <: HList, B <: HList](keys: In)(implicit
gen: Generic.Aux[In, A],
comapped: Comapped.Aux[A, Foo, B],
natTRel: NatTRel[A, Foo, B, Id],
tupler: Tupler[B]
): Foo[tupler.Out] =
Foo(tupler(natTRel.map(new (Foo ~> Id) { def apply[T](foo: Foo[T]) = foo.a }, gen.to(keys))))
val result = extract((Foo(1), Foo("a")))
def main(args: Array[String]): Unit = {
println(result)//Foo((1,a))
}
}
Maybe someone with a better understanding of shapeless can provide you a better answer. According to my understanding the problem lies at the type inference step. If you specify all the types explicitly as in
val result: Foo[(Int, String)] = extract[(Foo[Int], Foo[String]),
Foo[Int] :: Foo[String] :: HNil,
Int :: String :: HNil]((Foo(1), Foo("a")))
the code correctly typechecks. Obviously you don't want to specify those types explicitly though.
According to my understanding the compiler can't infer good B and tupler.Out because they are not coupled tight enough to In and A. One way you can work this around is by introducing an intermediate trait like this:
trait Extractor[L <: HList, HF] {
type FR <: HList
type TR
val folder: RightFolder.Aux[L, Foo[HNil], HF, Foo[FR]]
val tupler: Tupler.Aux[FR, TR]
}
object Extractor {
type Aux[L <: HList, HF, FR0 <: HList, TR0] = Extractor[L, HF] {type FR = FR0; type TR = TR0}
implicit def wrap[L <: HList, In, HF, FR0 <: HList, TR0](implicit folder0: RightFolder.Aux[L, Foo[HNil], HF, Foo[FR0]],
tupler0: Tupler.Aux[FR0, TR0]) = new Extractor[L, HF] {
type FR = FR0
type TR = TR0
override val folder = folder0
override val tupler = tupler0
}
}
and then using it like this:
def extract[In, A <: HList, B <: HList, C](keys: In)
(implicit gen: Generic.Aux[In, A],
extractor: Extractor.Aux[A, extractFold.type, B, C])
: Foo[C] = {
val hli = gen.to(keys)
val fr = extractor.folder(hli, Foo(HNil))
Foo(extractor.tupler(fr.a))
}
This is a hacky solution but at least it seem to work (see also online demo).

how to use shapeless to detect field type annotation

I am trying to gather the fields of a case class that have a particular annotations at compile time using shapeless. I tried to play around the following snippet, but it did not work as expected (output nothing instead of printing "i"). How can I make it work ?
import shapeless._
import shapeless.labelled._
final class searchable() extends scala.annotation.StaticAnnotation
final case class Foo(#searchable i: Int, s: String)
trait Boo[A] {
def print(a: A): Unit
}
sealed trait Boo0 {
implicit def hnil = new Boo[HNil] { def print(hnil: HNil): Unit = () }
implicit def hlist[K <: Symbol, V, RL <: HList](implicit b: Boo[RL]): Boo[FieldType[K, V] :: RL] =
new Boo[FieldType[K, V] :: RL] {
def print(a: FieldType[K, V] :: RL): Unit = {
b.print(a.tail)
}
}
}
sealed trait Boo1 extends Boo0 {
implicit def hlist1[K <: Symbol, V, RL <: HList](implicit annot: Annotation[searchable, K], witness: Witness.Aux[K], b: Boo[RL]): Boo[FieldType[K, V] :: RL] =
new Boo[FieldType[K, V] :: RL] {
def print(a: FieldType[K, V] :: RL): Unit = {
Console.println(witness.value.name)
b.print(a.tail)
}
}
}
object Boo extends Boo1 {
implicit def generics[A, HL <: HList](implicit iso: LabelledGeneric.Aux[A, HL], boo: Boo[HL]): Boo[A] =
new Boo[A] {
def print(a: A): Unit = {
boo.print(iso.to(a))
}
}
}
implicitly[Boo[Foo]].print(Foo(1, "2"))
Looking at the macro of Annotation, it rejects type that is not a product or coproduct straight up
val annTreeOpts =
if (isProduct(tpe)) { ... }
else if (isCoproduct(tpe)) { ... }
else abort(s"$tpe is not case class like or the root of a sealed family of types")
this is quite unfortunate, as collecting type annotations at per field symbol level could be quite useful sometimes.
There is another type class Annotations defined in the same file that can actually collect particular annotations on field into an HList. However problem is the field information is totally lost. There is a clumsy way to hack things together to serve my use case...
// A is our annotation
// B is our result type
// C is our case class with some fields annotated with A
def empty: B = ???
def concat(b1: B, b2: B): B = ???
def func(a: A, nm: String): B = ???
object Collector extends Poly2 {
implicit def some[K <: Symbol](implicit witness: Witness.Aux[K]) =
at[B, (K, Some[A])] { case (b, (_, a)) => concat(b, func(a.get, witness.value.name)) }
implicit def none[K <: Symbol] = at[B, (K, None.type)] { case (b, _) => b }
}
def collect[HL <: HList, RL <: HList, KL <: HList, ZL <: HList](implicit
iso: LabelledGeneric.Aux[C, HL]
, annot: Annotations.Aux[A, C, RL]
, keys: Keys.Aux[HL, KL]
, zip: Zip.Aux[KL :: RL :: HNil, ZL]
, leftFolder: LeftFolder.Aux[ZL, B, Collector.type, B]): B = {
zip(keys() :: annot() :: HNil).foldLeft(empty)(Collector)
}

Do a covariant filter on an HList

I intend to filter on an HList in a covariant manner - I would like to include subclasses as well. So the covariant filter on Foo should capture elements of Foo as well as Bar. I've constructed this example trying out <:!<, to see if it does what I would like it to do.
http://scastie.org/6465
/***
scalaVersion := "2.11.2"
libraryDependencies ++= Seq(
"com.chuusai" %% "shapeless" % "2.0.0"
)
*/
import shapeless._
final class HListOps[L <: HList](l: L) {
trait CoFilter[L <: HList, U] extends DepFn1[L] { type Out <: HList }
object CoFilter {
def apply[L <: HList, U](implicit filter: CoFilter[L, U]): Aux[L, U, filter.Out] = filter
type Aux[L <: HList, U, Out0 <: HList] = CoFilter[L, U] { type Out = Out0 }
implicit def hlistCoFilterHNil[L <: HList, U]: Aux[HNil, U, HNil] =
new CoFilter[HNil, U] {
type Out = HNil
def apply(l: HNil): Out = HNil
}
implicit def hlistCoFilter1[L <: HList, H](implicit f: CoFilter[L, H]): Aux[H :: L, H, H :: f.Out] =
new CoFilter[H :: L, H] {
type Out = H :: f.Out
def apply(l: H :: L): Out = l.head :: f(l.tail)
}
implicit def hlistCoFilter2[H, L <: HList, U](implicit f: CoFilter[L, U], e: U <:!< H): Aux[H :: L, U, f.Out] =
new CoFilter[H :: L, U] {
type Out = f.Out
def apply(l: H :: L): Out = f(l.tail)
}
}
def covariantFilter[U](implicit filter: CoFilter[L, U]): filter.Out = filter(l)
}
object Main extends App {
class Foo(val foo: Int)
class Bar(val bar: Int) extends Foo(bar)
val l = new Foo(1) :: new Bar(2) :: new Foo(3) :: new Bar(4) :: HNil
implicit def hlistOps[L <: HList](l: L): HListOps[L] = new HListOps(l)
print(l.covariantFilter[Bar] != l)
}
Gives me
[error] /tmp/rendererbI8Iwy0InO/src/main/scala/test.scala:47: could not find implicit value for parameter filter: _1.CoFilter[shapeless.::[Main.Foo,shapeless.::[Main.Bar,shapeless.::[Main.Foo,shapeless.::[Main.Bar,shapeless.HNil]]]],Main.Bar]
[error] print(l.covariantFilter[Bar] != l)
There are a couple of issues here. The first is that your type class is defined inside of your extension class, but you need the instance at the point where you're calling covariantFilter. Maybe the compiler could find it for you, but it doesn't. It's a lot cleaner not to nest the type class anyway, though.
The second issue is that your two hlistCoFilterN cases don't actually capture all the stuff you want. You only tell the compiler what to do in cases where the type of the head is the filter type and where the filter type is not a subtype of the type of the head. What about where the type of the head is a subtype of the filter type? You probably want something like this:
import shapeless._
trait CoFilter[L <: HList, U] extends DepFn1[L] { type Out <: HList }
object CoFilter {
def apply[L <: HList, U](implicit f: CoFilter[L, U]): Aux[L, U, f.Out] = f
type Aux[L <: HList, U, Out0 <: HList] = CoFilter[L, U] { type Out = Out0 }
implicit def hlistCoFilterHNil[L <: HList, U]: Aux[HNil, U, HNil] =
new CoFilter[HNil, U] {
type Out = HNil
def apply(l: HNil): Out = HNil
}
implicit def hlistCoFilter1[U, H <: U, T <: HList]
(implicit f: CoFilter[T, U]): Aux[H :: T, U, H :: f.Out] =
new CoFilter[H :: T, U] {
type Out = H :: f.Out
def apply(l: H :: T): Out = l.head :: f(l.tail)
}
implicit def hlistCoFilter2[U, H, T <: HList]
(implicit f: CoFilter[T, U], e: H <:!< U): Aux[H :: T, U, f.Out] =
new CoFilter[H :: T, U] {
type Out = f.Out
def apply(l: H :: T): Out = f(l.tail)
}
}
implicit final class HListOps[L <: HList](val l: L) {
def covariantFilter[U](implicit filter: CoFilter[L, U]): filter.Out = filter(l)
}
(For the record, you could also remove the H <:!< U constraint and move hlistCoFilter2 to a LowPriorityCoFilter trait. I find this version a little clearer about its intent, but getting rid of the constraint would arguably be cleaner.)
Now if you have the following:
class Foo(val foo: Int)
class Bar(val bar: Int) extends Foo(bar)
val l = new Foo(1) :: new Bar(2) :: new Foo(3) :: new Bar(4) :: HNil
Your filter will work like this:
scala> l.covariantFilter[Foo] == l
res0: Boolean = true
scala> l.covariantFilter[Bar] == l
res1: Boolean = false
Which I think is what you want.

use implicit to find "One" element of HList typeclass to inject

Here is the code:
trait Service[T<: HList] {
def doStuff(): Unit
}
class A
class B
class C
class ServiceAB extends Service[A :: B :: HNil] {
override def doStuff(): Unit = println("handling a b")
}
class ServiceC extends Service[C :: HNil] {
override def doStuff(): Unit = println("handling c")
}
implicit val serviceAB = new ServiceAB
implicit val serviceC = new ServiceC
def operate[T, W <: HList](x: T)(implicit service: Service[W]) = {
service.doStuff()
}
operate(new C)
I just wonder is it possible or what should I code in the type level to inject implicit serviceC when operate(new C) is executed, since class C is the element of HList of type class of ServiceC ?
Many thanks in advance
I realy don't know why you need this :)
So your code works, but if you pass type parameter explicitly:
operate[C, C :: HNil](new C)
If you want same, but implicitly, you can define your class type:
trait Service[L <: HList, U] { def doStuff(): Unit }
trait lowPriority {
implicit def otherwise[L <: HList, U] =
new Service[L, U] {
def doStuff(): Unit = println("handling otherwise")
}
}
object Service extends lowPriority {
implicit def ab[L <: HList, U]
(implicit e: L =:= (A :: B :: HNil),
s: Selector[L, U]) =
new Service[L, U] {
def doStuff(): Unit = println("handling a b")
}
implicit def c[L <: HList, U]
(implicit e: L =:= (C :: HNil),
s: Selector[L, U]) =
new Service[L, U] {
def doStuff(): Unit = println("handling c")
}
}
}
def operate[T, W <: HList](x: T)(implicit service: Service[W, T]) = {
service.doStuff()
}
So this works as expected:
operate(new C) //> handling c
operate(new A) //> handling a b
operate(new B) //> handling a b
It is possible to make it more general (so it will check is type you need is in a HList, ohterwise if it doesn't) (using Curry-Howard isomorphism, great article with explanations by Miles Sabin: http://www.chuusai.com/2011/06/09/scala-union-types-curry-howard/):
import reflect.runtime.universe._
type ¬[A] = A => Nothing
type ∨[T, U] = ¬[¬[T] with ¬[U]]
type ¬¬[A] = ¬[¬[A]]
class A
class B
class C
class D //> additional class for example
trait Service[L <: HList, U] { def doStuff(): Unit }
trait lowPriority {
implicit def otherwise[L <: HList, U] =
new Service[L, U] {
def doStuff(): Unit = println("handling otherwise")
}
}
object Service extends lowPriority {
implicit def ab[L <: HList, U]
(implicit e: (¬¬[U] <:< (A ∨ B)),
s: Selector[L, TypeTag[U]]) =
new Service[L, U] {
def doStuff(): Unit = println("handling a b")
}
implicit def c[L <: HList, U](implicit e: U =:= C, s: Selector[L, TypeTag[U]]) =
new Service[L, U] {
def doStuff(): Unit = println("handling c")
}
}
}
def operateBi[T, W <: HList](x: T, w: W)(implicit service: Service[W, T]) = {
service.doStuff()
}
Defining HLists of types:
val hl1 = implicitly[TypeTag[A]] :: implicitly[TypeTag[B]] :: HNil
val hl2 = implicitly[TypeTag[C]] :: HNil
operateBi(new C, hl1)
operateBi(new A, hl2)
operateBi(new B, hl1)
operateBi(new D, hl1)
Works as expected.