Is it possible to provide a generic function which would traverse an arbitrary case class hierarchy and collect information from selected fields? In the following snippet, such fields are encoded as Thing[T].
The snippet works fine for most scenarios. The only problem is when Thing wraps a type class (e.g. List[String]) and such field is nested deeper in the hierarchy; when it is on the top level, it works fine.
import shapeless.HList._
import shapeless._
import shapeless.ops.hlist.LeftFolder
case class Thing[T](t: T) {
def info: String = ???
}
trait Collector[T] extends (T => Seq[String])
object Collector extends LowPriority {
implicit def forThing[T]: Collector[Thing[T]] = new Collector[Thing[T]] {
override def apply(thing: Thing[T]): Seq[String] = thing.info :: Nil
}
}
trait LowPriority {
object Fn extends Poly2 {
implicit def caseField[T](implicit c: Collector[T]) =
at[Seq[String], T]((acc, t) => acc ++ c(t))
}
implicit def forT[T, L <: HList](implicit g: Generic.Aux[T, L],
f: LeftFolder.Aux[L, Seq[String], Fn.type, Seq[String]]): Collector[T] =
new Collector[T] {
override def apply(t: T): Seq[String] = g.to(t).foldLeft[Seq[String]](Nil)(Fn)
}
}
object Test extends App {
case class L1(a: L2)
case class L2(b: Thing[List[String]])
implicitly[Collector[L2]] // works fine
implicitly[Collector[L1]] // won't compile
}
I'm afraid that this is impossible. HList appears to be constructed compile time from statically known things. So, when you wrap your types, for whatever reason, it seems that the HList is unable to infer the proper implicits.
Here is a simple example built from shapeless's flatten example.
object Test extends App {
import shapeless._
import ops.tuple.FlatMapper
import syntax.std.tuple._
trait LowPriorityFlatten extends Poly1 {
implicit def default[T] = at[T](Tuple1(_))
}
object flatten extends LowPriorityFlatten {
implicit def caseTuple[P <: Product](implicit fm: FlatMapper[P, flatten.type]) =
at[P](_.flatMap(flatten))
}
case class AT[T](a: T, b: T)
case class A2T[T](a: AT[T], b: AT[T])
case class A2(a: AT[Int], b: AT[Int])
println(flatten(A2T(AT(1, 2), AT(3, 4))))
println(flatten(A2(AT(1, 2), AT(3, 4))))
}
You would think that this should print out the same thing for A2T and A2, however it does not. It actually prints out:
(1,2,3,4)
(AT(1,2),AT(3,4))
So, I do not think you can use Shapeless to do what you want.
However! You can still walk your case class hierarchy looking for Things (just not with shapeless). Check this out!
object Test extends App {
case class Thing[T](t: T) {
def info: String = toString
}
def collect[T](t: T): Iterator[String] = t match {
case t: Thing[_] => Iterator(t.info)
case p: Product => p.productIterator.flatMap(collect)
case _ => Iterator()
}
case class L0(a: L1)
case class L1(a: L2)
case class L2(a: Thing[List[String]])
case class MT(a: L2, b: L2, c: Thing[Int])
println("Case #1")
collect(L0(L1(L2(Thing(List("a", "b", "c")))))).foreach(println)
println("Case #2")
collect(MT(L2(Thing(List("a", "c"))), L2(Thing(List("b"))), Thing(25))).foreach(println)
}
This has output:
Case #1
Thing(List(a, b, c))
Case #2
Thing(List(a, c))
Thing(List(b))
Thing(25)
Related
I'm trying to convert a case class into another via conversion to HList.
case class Source(p1:S1, p2:S2) -> HList[S1:+:S2] -> HList[D1:+:D2] ->case class Destination(p1:D1,p2:D2)
I can convert from Source to HList via gem.to and from HList to Destination via gen.from.
I wrote a Converter for each type of parameter on Source to convert it to its corresponding type in Destination but I am unsure how to recursively traverse the HList. My attempt is shown below in hlistEncoder
trait Converter[T] {
def convert(t:T): Datastructure
}
object Converter {
implicit object StrDatastructure extends Converter[String]{
def convert(t:String) = Datastructure.Str(t)
}
implicit object NumDatastructure extends Converter[Double]{
def convert(t :Double) = Datastructure.Num(t)
}
implicit object IncDatastructure extends Converter[Inc]{
def convert(t :Inc) = Datastructure.Incc(t)
}
implicit def SeqDatastructure[T: Converter]: Converter[Seq[T]] = new Converter[Seq[T]]{
def convert(t: Seq[T]) = {
Datastructure.Listt(t.map(implicitly[Converter[T]].convert):_*)
}
}
//HList traversals
implicit object hnilDatastructure extends Converter[HNil]{
def convert(t: HNil) = Datastructure.Hnill(t)
}
implicit def hlistEncoder[H, T <: HList](implicit
hEncoder: Converter[H],
tEncoder: Converter[T]
): Converter[H :: T] = new Converter[H :: T] {
def apply(h:H, t:T)= {
case (h :: t) => hEncoder.convert(h) ++ tEncoder.convert(t)
}
}
}
I use this method to test HList to HList conversion
def convertToDatastructureN[T](x: T)(implicit converter: Converter[T]): Datastructure = {
converter.convert(x)
}
case class Inc(i:Int)
case class Source(i: Int, n:Inc)
val x = Generic[Source]
val xHlist = x.to(Source(99, Inc(5)))
convertToDatastructureN(xHlist)
Any ideas how to implement hlistEncoder?
I guess you have
sealed trait Datastructure
object Datastructure {
case class Str(t: String) extends Datastructure
case class Num(t: Double) extends Datastructure
case class Incc(t: Inc) extends Datastructure
case class Listt(t: Datastructure*) extends Datastructure
case class Hnill(t: HNil) extends Datastructure
}
You want your type class Converter to transform T into Datastructure. But also (HList[S1:+:S2] -> HList[D1:+:D2], where I guess :: should be instead of :+:) you want subtypes of HList to be transformed into subtypes of HList (not into HList itself since otherwise Generic can't restore case class). So either you should modify your type class
trait Converter[T] {
type Out
def convert(t:T): Out
}
or you need two type classes: your original Converter and
trait HListConverter[T <: HList] {
type Out <: HList
def convert(t:T): Out
}
Moreover, currently your Converter is pretty rough. It transforms every T into Datastructure instead of into specific subtypes of Datastructure. This means Generic will be able to restore case classes only of shapes
MyClass(x: Datastructure)
MyClass(x: Datastructure, y: Datastructure)
...
Is it really what you want? If so then ok, if not and you need
MyClass(x: Str)
MyClass(x: Num, y: Incc)
...
then again you need
trait Converter[T] {
type Out
def convert(t:T): Out
}
Instead of HListConverter you can use standard shapeless.ops.hlist.Mapper.
I want to write a type class, to add some behavior to a generic type. However, I cannot figure out how to do it; I keep running into the error below.
Imagine you have a generic type MyList[A]:
trait MyList[A]
object MyList {
case class Empty[A]() extends MyList[A]
case class Node[A](value: A, next: MyList[A]) extends MyList[A]
}
Now you want to add some behavior to this class, e.g. to convert it into a Stream[A]. A type class based extension would seem appropriate:
// inspired by https://scalac.io/typeclasses-in-scala
trait MyListExt[T, A <: MyList[T]] {
def stream(a: A): Stream[T]
}
object MyListExt {
def apply[T, A <: MyList[T]](implicit a: MyListExt[T, A]): MyListExt[T, A] = a
object ops {
implicit class MyListExtOps[T, A <: MyList[T]](val a: A) extends AnyVal {
def stream(implicit ext: MyListExt[T, A]): Stream[T] = ext.stream(a)
}
}
private type T0
implicit val myListToStreamImpl: MyListExt[T0, MyList[T0]] = new MyListExt[T0, MyList[T0]] {
override def stream(a: MyList[T0]): Stream[T0] = {
def fold[T1](l: MyList[T1], acc: Stream[T1]): Stream[T1] = l match {
case MyList.Empty() => acc
case MyList.Node(value, next) => fold(next, acc :+ value)
}
fold(a, Stream.empty)
}
}
}
When I now try to use this type class in my code, I get the following error at l.stream:
No implicits found for parameter ext: MyListExt[T_, MyList[Int]]
object MyListTest {
def main(args: Array[String]): Unit = {
import MyListExt.ops._
val l: MyList[Int] = MyList.Node(1, MyList.Node(2, MyList.Node(3, MyList.Empty())))
l.stream.foreach(println)
}
}
What am I doing wrong, or how can I get my l.stream to work?
I have seen many examples involving type classes and implicit conversion, but none so far operating on generic types.
implicit def myListToStreamImpl[T]: MyListExt[T, MyList[T]] = new MyListExt[T, MyList[T]] {
override def stream(a: MyList[T]): Stream[T] = {
def fold(l: MyList[T1], acc: Stream[T1]): Stream[T1] = l match {
case MyList.Empty() => acc
case MyList.Node(value, next) => fold(next, acc :+ value)
}
fold(a, Stream.empty[T1])
}
}
Your types don't align because you've used that private type for whatever bizarre reason. Types nested inside objects have a completely different application, and they don't relate to your current use case.
The trouble is that in l.stream.foreach(println) the l is implicitly transformed to new MyListExt.ops.MyListExtOps[.., ..](l) and generics are inferred to be [Nothing, MyList[Int]], which doesn't satisfy [T, A <: MyList[T]].
I can't see reason to parametrize MyListExt with both T and A <: MyList[T]. I guess T is enough, use MyList[T] instead of A.
Don't use private type T0, just parametrize myListToStreamImpl (make it def) with T0 aka T.
Try
trait MyList[A]
object MyList {
case class Empty[A]() extends MyList[A]
case class Node[A](value: A, next: MyList[A]) extends MyList[A]
}
trait MyListExt[T] {
def stream(a: MyList[T]): Stream[T]
}
object MyListExt {
def apply[T](implicit a: MyListExt[T]): MyListExt[T] = a
object ops {
implicit class MyListExtOps[T](val a: MyList[T]) extends AnyVal {
def stream(implicit ext: MyListExt[T]): Stream[T] = ext.stream(a)
}
}
implicit def myListToStreamImpl[T]: MyListExt[T] = new MyListExt[T] {
override def stream(a: MyList[T]): Stream[T] = {
def fold[T1](l: MyList[T1], acc: Stream[T1]): Stream[T1] = l match {
case MyList.Empty() => acc
case MyList.Node(value, next) => fold(next, acc :+ value)
}
fold(a, Stream.empty)
}
}
}
object MyListTest {
def main(args: Array[String]): Unit = {
import MyListExt.ops._
val l: MyList[Int] = MyList.Node(1, MyList.Node(2, MyList.Node(3, MyList.Empty())))
l.stream.foreach(println)
}
}
Using scala 2.12.8 this would not compile without a cast:
trait Content
case object A extends Content
case class B(i: Int) extends Content
def asList[C <: Content](content: C): List[C] = content match {
case A => List(A) // compiles
case b: B => List(b) // does not compile
}
type mismatch;
found : b.type (with underlying type Playground.this.B)
required: C
Here's a Scastie link to the problem: https://scastie.scala-lang.org/JIziYOYNTwKoZpdCIPCvdQ
Why is working for the case object and not for the case class? How can I make it work for the case class?
EDIT
The first answers made me realize I oversimplified my problem, here's an updated version :
sealed trait Content
case object A extends Content
final case class B(i: Int) extends Content
sealed trait Container[+C <: Content]
case class ContainerA(content: A.type) extends Container[A.type]
case class ContainerB(content: B) extends Container[B]
object Container {
def apply[C <: Content](content: C): Container[C] = content match {
case A => ContainerA(A) // compiles
case b: B => ContainerB(b) // does not compile
}
}
Scastie link: https://scastie.scala-lang.org/TDlJM5SYSwGl2gmQPvKEXQ
C cannot be a subtype of B since B is final.
The solution is given in the comment by #lasf:
def asList[C <: Content](content: C): List[C] = content match {
case A => List(A) // compiles
case b: B => List(content) // compiles
}
The problem is that the return type is List[C] but the compiler cannot guarantee that the type of List(b) is List[C]. In particular, C could be a subtype of B in which case List(b) would List[B] which is not compatible with List[C].
The updated version can be solved using asInstanceOf, though it is not pretty.
def apply[C <: Content](content: C): Container[C] = content match {
case A => ContainerA(A) // compiles
case b: B => ContainerB(b).asInstanceOf[Container[C]]
}
Alternatively, you could take a different approach and use implicit conversion:
object Container {
implicit def contain(content: A.type): Container[A.type] = ContainerA(content)
implicit def contain(content: B): Container[B] = ContainerB(content)
}
val ca: Container[A.type] = A
val cb: Container[B] = B(0)
Or even multiple constructors:
object Container {
def apply(content: A.type): Container[A.type] = ContainerA(content)
def apply(content: B): Container[B] = ContainerB(content)
}
Here is an alternative design using a typeclass. This replaces the Content superclass with a Containable typeclass. The Container class can now contain anything as long as there is an instance of Containable for that class.
case object A
case class B(i: Int)
sealed trait Container[C]
case class ContainerA(content: A.type) extends Container[A.type]
case class ContainerB(content: B) extends Container[B]
trait Containable[T] {
def apply(value: T): Container[T]
}
object Containable {
implicit object AContainer extends Containable[A.type] {
def apply(value: A.type) = ContainerA(value)
}
implicit object BContainer extends Containable[B] {
def apply(value: B) = ContainerB(value)
}
}
object Container {
def apply[C](content: C)(implicit containable: Containable[C]): Container[C] =
containable(content)
}
C cannot be a subtype of B since B is final.
Wrong!
Singleton types of B instances are subtypes of B:
val b = B(0)
val container: Container[b.type] = Container[b.type](b)
Since ContainerB doesn't extend Container[b.type], it can't be returned by the last line. And it can't be changed so that it does;
case class ContainerB(content: B) extends Container[content.type]
is not legal in Scala.
Null is also a subtype of B and you can create a similar example. And so are refinement types like B { type T = Int }.
Other subtypes which are probably irrelevant because they don't have instances: Nothing, compound types like B with Iterable[Int]...
The reason why you are getting an error is because of the return type of the method is not explicit. On replacing the return type from List[C] to List[Content] solves the problem.
def asList[C <: Content](content: C): List[Content] = content match {
case A => List(A) // compiles
case b: B => List(b) // compiles
}
I am trying to create a type class that will allow me to increment an Int field called "counter" on any case class, as long as that class has such a field.
I have tried to do this with Shapeless but am hitting walls (after first trying to digest "The Type Astronaut's Guide to Shapeless", the "Feature overview" of Shapeless 2.0.0 and numerous threads on Stack Overflow).
What I want is to be able to do something like
case class MyModel(name:String, counter:Int) {}
val instance = MyModel("Joe", 4)
val incremented = instance.increment()
assert(incremented == MyModel("Joe", 5))
And it should work for any case class with a suitable counter field.
I thought that this would be possible using a type class and Shapeless' record abstraction (and an implicit conversion to get the increment functionality added as a method). The bare bones would be something like this:
trait Incrementer[T] {
def inc(t:T): T
}
object Incrementer {
import shapeless._ ; import syntax.singleton._ ; import record._
implicit def getIncrementer[T](implicit generator: LabelledGeneric[T]): Incrementer[T] = new Incrementer[T] {
def inc(t:T) = {
val repr = generator.to(t)
generator.from(repr.replace('counter, repr.get('counter) + 1))
}
}
}
However, this does not compile. The error being value replace is not a member of generator.Repr. I guess this is because the compiler does not have any guarantee as to T having a field called counter and it being of type Int. But how could I tell it so? Is there better/more documentation on Shapeless' record? Or is this a completely wrong way to go?
You have to just implicitly require a Modifier
import shapeless._
import ops.record._
implicit class Incrementer[T, L <: HList](t: T)(
implicit gen: LabelledGeneric.Aux[T, L],
modifier: Modifier.Aux[L, Witness.`'counter`.T, Int, Int, L]
) {
def increment(): T = gen.from(modifier(gen.to(t), _ + 1))
}
You can easily do it with a simple typeclass derivation:
trait Incrementer[T] {
def inc(s: Symbol)(t: T): T
}
object Incrementer {
def apply[T](implicit T: Incrementer[T]): Incrementer[T] = T
implicit def head[Key <: Symbol, Head, Tail <: HList](implicit Key: Witness.Aux[Key], Head: Numeric[Head]) = new Incrementer[FieldType[Key, Head] :: Tail] {
import Head._
override def inc(s: Symbol)(t: FieldType[Key, Head] :: Tail): (FieldType[Key, Head] :: Tail) =
if (s == Key.value) (t.head + fromInt(1)).asInstanceOf[FieldType[Key, Head]] :: t.tail
else t
}
implicit def notHead[H, Tail <: HList](implicit Tail: Incrementer[Tail]) = new Incrementer[H :: Tail] {
override def inc(s: Symbol)(t: H :: Tail): H :: Tail = t.head :: Tail.inc(s)(t.tail)
}
implicit def gen[T, Repr](implicit gen: LabelledGeneric.Aux[T, Repr], Repr: Incrementer[Repr]) = new Incrementer[T] {
override def inc(s: Symbol)(t: T): T = gen.from(Repr.inc(s)(gen.to(t)))
}
}
case class Count(counter: Int)
case class CountAndMore(more: String, counter: Int)
case class FakeCount(counter: Long)
object Test extends App {
println(Incrementer[Count].inc('counter)(Count(0)))
println(Incrementer[CountAndMore].inc('counter)(CountAndMore("", 0)))
println(Incrementer[FakeCount].inc('counter)(FakeCount(0)))
}
An example case class: Was trying to create a generic query builder
case class BaseQuery(operand: String, value: String)
case class ContactQuery(phone: BaseQuery, address: BaseQuery)
case class UserQuery(id: BaseQuery, name: BaseQuery, contact: ContactQuery)
val user = UserQuery(BaseQuery("equal","1"), BaseQuery("like","Foo"), ContactQuery(BaseQuery("eq","007-0000"),BaseQuery("like", "Foo City")))
//case class Contact(phone: String, address: String)
//case class User(id: Long, name: String, contact: Contact)
//val user = User(1, "Foo Dev", Contact("007-0000","Foo City"))
How can we retrieve the field names and respective values in scala,
On further research,
Solutions using Scala Reflection:
def classAccessors[T: TypeTag]: List[MethodSymbol] = typeOf[T].members.collect {
case m: MethodSymbol if m.isCaseAccessor => m
}.toList
// The above snippet returns the field names, and as input we have to
//feed the 'TypeTag' of the case class. And since we are only feeding a
//TypeTag we will not have access to any object values. Is there any Scala
// Reflection variant of the solution.
Another solution,
def getMapFromCaseClass(cc: AnyRef) =
(scala.collection.mutable.Map[String, Any]() /: cc.getClass.getDeclaredFields)
{
(a, f) =>
f.setAccessible(true)
a + (f.getName -> f.get(cc))
}
// The above snippet returns a Map[String, Any] if we feed the case class
//object. And we will need to match the map value to any of our other case
//classes. If the class structure were simple, the above solution would be
//helpful, but in case of complex case class this would not be the most efficient solution.
Trying to:
I am actually trying to retrieve the list of field and their values during runtime.
One of the use cases,
val list: Seq[Somehow(Field+Value)] = listingFieldWithValue(obj: UserQuery)
for(o <- list){
o.field.type match{
case typeOne: FooType =>{
//Do something
}
case typeTwo: FooBarType =>{
//Do something else
}
}
}
Not really scala-reflect solution, but shapeless one. Shapeless is built on top of implicit macro, so it way faster than any reflection, if you have type information at compile time.
For implementation of your updated requirements you'll need some time to carry needed type information
sealed trait TypeInfo[T]{
//include here thing you like to handle at runtime
}
Next you can define such info for different types and provide implicit resolution. In my example such implementation are also suitable for later matching
implicit case object StringField extends TypeInfo[String]
case class NumericField[N](implicit val num: Numeric[N]) extends TypeInfo[N]
implicit def numericField[N](implicit num: Numeric[N]): TypeInfo[N] = new NumericField[N]
case class ListField[E]() extends TypeInfo[List[E]]
implicit def listField[E] = new ListField[E]
Then we define more suitable for pattern matching result structure
sealed trait FieldDef
case class PlainField[T](value: Any, info: TypeInfo[T]) extends FieldDef
case class EmbeddedType(values: Map[Symbol, FieldDef]) extends FieldDef
So any result will be either value container with needed type info or container for deeper look.
Finally we can define concept implementation
import shapeless._
import shapeless.labelled.FieldType
import shapeless.ops.hlist.{ToTraversable, Mapper}
sealed abstract class ClassAccessors[C] {
def apply(c: C): Map[Symbol, FieldDef]
}
trait LowPriorClassInfo extends Poly1 {
implicit def fieldInfo[K <: Symbol, V](implicit witness: Witness.Aux[K], info: TypeInfo[V]) =
at[FieldType[K, V]](f => (witness.value: Symbol, PlainField(f, info)))
}
object classInfo extends LowPriorClassInfo {
implicit def recurseInfo[K <: Symbol, V](implicit witness: Witness.Aux[K], acc: ClassAccessors[V]) =
at[FieldType[K, V]](f => (witness.value: Symbol, EmbeddedType(acc(f))))
}
implicit def provideClassAccessors[C, L <: HList, ML <: HList]
(implicit lgen: LabelledGeneric.Aux[C, L],
map: Mapper.Aux[classInfo.type, L, ML],
toList: ToTraversable.Aux[ML, List, (Symbol, FieldDef)]) =
new ClassAccessors[C] {
def apply(c: C) = toList(map(lgen.to(c))).toMap
}
def classAccessors[C](c: C)(implicit acc: ClassAccessors[C]) = acc(c)
now
classAccessors(User(100, "Miles Sabin", Contact("+1 234 567 890", "Earth, TypeLevel Inc., 10")))
will result to
Map(
'id -> PlainField(100, NumericField( scala.math.Numeric$LongIsIntegral$#...)),
'name -> PlainField("Miles Sabin", StringField),
'contact -> EmbeddedType( Map(
'phone -> PlainField( "+1 234 567 890", StringField),
'address -> PlainField("Earth, TypeLevel Inc., 10", StringField))))