Converting a case class hierarchy to scalaz.Tree with Shapeless - scala

Consider the following hierarchy:
sealed trait Ex
case class Lit(value: Int) extends Ex
case class Var(name: Char) extends Ex
case class Add(a: Ex, b: Ex) extends Ex
I'd like to convert expressions such as val ex = Add(Lit(0),Add(Lit(1),Var('a'))) into a scalaz.Tree[Either[Class[Any],Any]]], which in this case would give:
-\/(class Add)
|
+- -\/(class Lit)
| |
| `- \/-(0)
|
`- -\/(class Add)
|
+- -\/(class Lit)
| |
| `- \/-(1)
|
`- -\/(class Var)
|
`- \/-(a)
Which is the most appropriate Shapeless functionality/example to start from for doing this?

First for a disclaimer: if you're planning to use this in real code, I think it's almost certainly a bad idea. It'd be better to try to use Shapeless to do what you're trying to do directly rather than going through this type-unsafe representation. But it's a fun problem, so here goes.
(Oh, and another disclaimer: this implementation is off the top of head and there may be nicer ways to accomplish this.)
First for a helper type class (note that all of the code in the three sections below will need to be defined together—you can use :paste if you're in a REPL):
import scalaz.{ Show, Tree, \/ }, scalaz.syntax.either._
import shapeless._, ops.hlist.ToTraversable
trait TreeifyCc[A, C] {
def apply(tf: Treeify[A], c: C): Tree[Class[_] \/ Any]
}
trait LowPriorityTreeifyCc {
implicit def singleMemberTreeifyCc[A, C, R <: HList, X](implicit
gen: Generic.Aux[C, R],
ev: R <:< (X :: HNil)
): TreeifyCc[A, C] = new TreeifyCc[A, C] {
def apply(tf: Treeify[A], c: C): Tree[Class[_] \/ Any] = Tree.Node(
c.getClass.left,
Stream(Tree.Leaf(ev(gen.to(c)).head.right))
)
}
}
object TreeifyCc extends LowPriorityTreeifyCc {
implicit def recursiveTreeifyCc[A, C, R <: HList](implicit
gen: Generic.Aux[C, R],
ts: ToTraversable.Aux[R, Stream, A]
): TreeifyCc[A, C] = new TreeifyCc[A, C] {
def apply(tf: Treeify[A], c: C): Tree[Class[_] \/ Any] =
Tree.Node(c.getClass.left, ts(gen.to(c)).map(tf(_)))
}
}
And another helper type class:
trait TreeifyAdt[A, C] {
def apply(tf: Treeify[A], c: C): Tree[Class[_] \/ Any]
}
object TreeifyAdt {
implicit def cnilTreeifyAdt[A]: TreeifyAdt[A, CNil] =
new TreeifyAdt[A, CNil] {
def apply(tf: Treeify[A], c: CNil): Tree[Class[_] \/ Any] =
sys.error("impossible")
}
implicit def cconsAdt[A, H, T <: Coproduct](implicit
cc: TreeifyCc[A, H],
ta: TreeifyAdt[A, T]
): TreeifyAdt[A, H :+: T] = new TreeifyAdt[A, H :+: T] {
def apply(tf: Treeify[A], c: H :+: T): Tree[Class[_] \/ Any] = c match {
case Inl(h) => cc(tf, h)
case Inr(t) => ta(tf, t)
}
}
}
And the type class we actually care about:
trait Treeify[A] {
def apply(a: A): Tree[Class[_] \/ Any]
}
object Treeify {
implicit def treeifyAdt[A, R <: Coproduct](implicit
gen: Generic.Aux[A, R],
adt: TreeifyAdt[A, R]
): Treeify[A] = new Treeify[A] {
def apply(a: A): Tree[Class[_] \/ Any] = adt(this, gen.to(a))
}
def toTree[A](a: A)(implicit tf: Treeify[A]): Tree[Class[_] \/ Any] = tf(a)
}
And we can use it like this:
scala> val ex: Ex = Add(Lit(0), Add(Lit(1), Var('a')))
ex: Ex = Add(Lit(0),Add(Lit(1),Var(a)))
scala> Treeify.toTree(ex).drawTree(scalaz.Show.showFromToString)
res0: String =
"-\/(class Add)
|
+- -\/(class Lit)
| |
| `- \/-(0)
|
`- -\/(class Add)
|
+- -\/(class Lit)
| |
| `- \/-(1)
|
`- -\/(class Var)
|
`- \/-(a)
"
This will work for any ADT where all of the leaves either have a single member or one or more recursive members.

Related

CanBuildFrom and upper type bound parameters

Here is a minimalistic example:
def map[U[_] <: Traversable[T], T](col: U[T]): U[T]
= col.map(identity)
This code does not compile.
type mismatch;
found : Traversable[T]
required: U[T]
= col.map(identity)
My understanding of the problem is the col.map(identity) where the compiler infers an implicit CanBuildFrom[U[T], T, Traversable[T]] instead of desired CanBuildFrom[U[T], T, U[T]].
Is there a way to make this code working?
The mechanics of builder type classes in Scala 2.12 is something like so
def foo[CC[x] <: Traversable[x], A, B](as: CC[A], f: A => B)(
implicit cbf: CanBuildFrom[CC[A], B, CC[B]]
): CC[B] = {
val bf = cbf()
as.foreach { a => bf += f(a) }
bf.result()
}
or using .to method
def foo[CC[x] <: Traversable[x], A, B](as: CC[A], f: A => B)(
implicit cbf: CanBuildFrom[CC[A], B, CC[B]]
): CC[B] = {
as.map(f).to[CC]
}
Corresponding Scala 2.13 code might be
def foo[CC[x] <: Iterable[x], A, B](as: CC[A], f: A => B)(
implicit cbf: BuildFrom[CC[A], B, CC[B]]
): CC[B] = {
cbf.fromSpecific(as)(as.map(f))
}
or using factory
def foo[CC[x] <: Iterable[x], A, B](as: CC[A], f: A => B)(
implicit factory: Factory[B, CC[B]]
): CC[B] = {
as.map(f).to(factory) // equivalent to factory.fromSpecific(as.map(f))
}
The meanings of three type parameters of collection builder type class are
| source coll | element of target coll | target coll |
---------------------------------------------------------------------
CanBuildFrom | -From | -Elem | +To |
BuildFrom | -From | -A | +C |
Factory | | -A | +C |
Note these builder type classes are just generalised version of the following idea
scala> val bf = List.newBuilder[Int]
val bf: scala.collection.mutable.Builder[Int,List[Int]] = ListBuffer()
scala> bf += 1 += 4
val res0: bf.type = ListBuffer(1, 4)
scala> bf.result()
val res1: List[Int] = List(1, 4)
but in practice we rarely see collections constructed in such way.
Another option instead of BuildFrom is to use cats type classes such as Functor.

shapeless convert case class to HList and skip all option fields

I have the next class:
case class Foo(a: Option[Int], b: Option[String], c: Option[Double])
as you can see, all fields is optional, i want convert this class into HList or Tuple, like
val f1 = Foo(Some(1) , None, Some(3D))
val f2 = Foo(None, "foo")
val result1 = f1.to[Int::Double::HNil] // => 1::3D
val result2 = f2.to[String::HNil] // "foo"
is it possible, without reflection?
It might be possible to do this with existing type classes in Shapeless (something like NatTRel and RemoveAll), but I'm not 100% sure of that, and this is a case where I'd just write my own type class:
import shapeless._
trait OptionalPieces[L <: HList, S <: HList] {
def apply(l: L): Option[S]
}
object OptionalPieces extends LowPriorityOptionalPieces {
implicit val hnilOptionalPieces: OptionalPieces[HNil, HNil] =
new OptionalPieces[HNil, HNil] {
def apply(l: HNil): Option[HNil] = Some(HNil)
}
implicit def hconsOptionalPiecesMatch[H, T <: HList, S <: HList](implicit
opt: OptionalPieces[T, S]
): OptionalPieces[Option[H] :: T, H :: S] =
new OptionalPieces[Option[H] :: T, H :: S] {
def apply(l: Option[H] :: T): Option[H :: S] = for {
h <- l.head
t <- opt(l.tail)
} yield h :: t
}
}
sealed class LowPriorityOptionalPieces {
implicit def hconsOptionalPiecesNoMatch[H, T <: HList, S <: HList](implicit
opt: OptionalPieces[T, S]
): OptionalPieces[Option[H] :: T, S] =
new OptionalPieces[Option[H] :: T, S] {
def apply(l: Option[H] :: T): Option[S] = opt(l.tail)
}
}
This witnesses that L contains at least all of the elements of S wrapped in Option, in order, and gives you a way to unwrap them at runtime (safely).
We can then define a syntax helper class like this:
implicit class OptionalPiecesSyntax[A, R <: HList](a: A)(implicit
gen: Generic.Aux[A, R]
) {
def to[S <: HList](implicit op: OptionalPieces[gen.Repr, S]): Option[S] =
op(gen.to(a))
}
And then:
scala> val f1 = Foo(Some(1) , None, Some(3D))
f1: Foo = Foo(Some(1),None,Some(3.0))
scala> val f2 = Foo(None, Some("foo"), None)
f2: Foo = Foo(None,Some(foo),None)
scala> val result1 = f1.to[Int :: Double :: HNil]
result1: Option[shapeless.::[Int,shapeless.::[Double,shapeless.HNil]]] = Some(1 :: 3.0 :: HNil)
scala> val result2 = f2.to[String :: HNil]
result2: Option[shapeless.::[String,shapeless.HNil]] = Some(foo :: HNil)
If you really wanted exceptions, you could just call .get in the syntax class, but that seems like a bad idea.

Poly function from monomorphic functions that operate on coproduct

I'm working on an API that should enable to build a shapeless Poly1 function dynamically from standard monomorphic functions that operate on types of some coproduct.
The goal is to expose a simple method that receives a function as:
type FooCoproduct = Foo :+: Bar :+: CNil
def addF[E](f: E => E)(implicit ev: Inject[FooCoproduct, E]) = ???
and accumulate these functions in order to build a total Poly1 function covering all types in the coproduct. The evidence ev here is to force that the type paremeter E is a type in the coproduct.
After testing several approaches, including generic derivation of typeclasses, the most promising one has led me to accumulate these monomorphic functions in an HList and try to resolve the one that applies by means of a Selector. This is probably better understood by example:
object CoproductSample extends App {
import shapeless.{ :+:, CNil, Coproduct, HList, HNil, Poly1 }
import shapeless.ops.coproduct.Inject
import shapeless.ops.hlist.Selector
class Builder[A <: Coproduct] {
def accum[B](f: B => B, hl: HList)(implicit ev: Inject[A, B]) = f :: hl
class PolyBuilder[L <: HList](hl: L) extends Poly1 {
implicit def run[T](implicit ev: Selector[L, T => T]) =
at[T](hl.select[T => T])
}
}
type Cop = Int :+: String :+: CNil
val builder = new Builder[Cop]
val hl1 = builder.accum((i: Int) => i + 1, HNil)
val hl2 = builder.accum((s: String) => s + "one", hl1)
object pf extends builder.PolyBuilder(hl2)
val rInt = Coproduct[Cop](10).fold(pf)
val rStr = Coproduct[Cop]("ten").fold(pf)
}
This code doesn't compile with the message:
could not find implicit value for parameter folder:
shapeless.ops.coproduct.Folder[CoproductSample.pf.type, CoproductSample.Cop]
I suppose that I need to provide a Selector[L, T => T] where L is the type of the accumulated HList but I can't come up with the way to do this. On the other hand, I have the feeling that there must be a simpler solution to my problem.
Any help would be appreciated.
Update
After doing some more research I've come up with a solution that almost works. Unfortunately I'm not able to track the result type properly.
object CoproductSample {
import shapeless.{ CNil, Coproduct, HList, HNil, Inl, Inr, Poly2, ::, :+: }
// Accumulates ordinary functions from A => A in an HList
def accum[A, L <: HList](f: A => A, hl: L): (A => A) :: L = f :: hl
// A poly2 function that evaluates some monomorphic function present in
// an HList for certain value that satifies the signature of this function
object PolyEval extends Poly2 {
implicit def hnilCase[A]: Case.Aux[A, HNil, Option[A]] =
at[A, HNil]((a, l) => None)
implicit def hheadCaseSuccess[A, T <: HList]: Case.Aux[A, (A => A) :: T, Option[A]] =
at[A, (A => A) :: T]((a: A, l: (A => A) :: T) => Option(l.head(a)))
implicit def hheadCaseFail[A, H, T <: HList](
implicit tail: Case.Aux[A, T, Option[A]]
): Case.Aux[A, (H => H) :: T, Option[A]] =
at[A, (H => H) :: T]((a: A, l: (H => H) :: T) => PolyEval(a, l.tail))
}
// A poly2 function that uses `PolyEval` for evaluating a value present in
// a coproduct against an HList of monomorphic functions
object PolyEvalCop extends Poly2 {
implicit def cnilCase[A <: CNil, L <: HList]: Case.Aux[A, L, Option[A]] =
at[A, L]((a, l) => sys.error("Impossible!"))
implicit def cconsCase[H, T <: Coproduct, L <: HList](
implicit head: PolyEval.Case.Aux[H, L, Option[H]],
tail: Case[T, L]) // What is the return type here???)
= at[H :+: T, L]((c, l) =>
c match {
case Inl(h) => PolyEval(h, l)
case Inr(t) => PolyEvalCop(t, l)
})
}
}
Console session:
scala> import shapeless._, CoproductSample._
import shapeless._
import CoproductSample._
scala> case class Foo(i: Int); case class Bar(s: String)
defined class Foo
defined class Bar
scala> val f = (foo: Foo) => foo.copy(i = foo.i * 2)
f: Foo => Foo = <function1>
scala> val g = (bar: Bar) => bar.copy(s = bar.s + "_changed!")
g: Bar => Bar = <function1>
scala> val hl = accum(g, accum(f, HNil))
hl: shapeless.::[Bar => Bar,shapeless.::[Foo => Foo,shapeless.HNil.type]] = <function1> :: <function1> :: HNil
scala> type C = Foo :+: Bar :+: CNil
defined type alias C
scala> PolyEvalCop(Coproduct[C](Foo(10)), hl)
res1: Any = Some(Foo(20))
scala> PolyEvalCop(Coproduct[C](Bar("bar")), hl)
res2: Any = Some(Bar(bar_changed!))
The result type is not properly tracked and it's resolved as Any.
From the signature of addF it looks like you want to map over the Coproduct, i.e. modify it's value and stay on a coproduct, i.e. def add[E](e:E):E, if that were the case this would work:
# {
trait Add[E]{
def add(e:E):E
}
object Add{
def apply[E:Add]:Add[E] = implicitly[Add[E]]
implicit object cnil extends Add[CNil] {
def add(e:CNil) = throw new RuntimeException("Impossible")
}
implicit def coproduct[H, T <: Coproduct](
implicit
addH: Add[H],
addT:Add[T],
basis: ops.coproduct.Basis[H :+: T,T]
):Add[H :+: T] = new Add[H :+: T]{
def add(e: H :+: T) = e match {
case Inl(h) => Coproduct[H :+: T](addH.add(h)) // to stay in the Coproduct
case Inr(t) => addT.add(t).embed[H :+: T] // to stay in the coproduct
}
}
}
}
defined trait Add
defined object Add
# implicit def addString = new Add[String] {
def add(e:String) = e + "-ah"
}
defined function addString
# implicit def addInt = new Add[Int] {
def add(e:Int) = e + 1
}
defined function addInt
# type C = Int :+: String :+: CNil
defined type C
# val i = Coproduct[C](1)
i: C = 1
# Add[C].add(i)
res24: C = 2 // notice that the return type is C
# val s = Coproduct[C]("a")
s: C = a
# Add[C].add(s)
res26: C = a-ah // notice that the return type is C
It obviously works with "plain" types:
# Add[Int].add(1)
res38: Int = 2
The above is equivalent to map; but if you want a fold, i.e. def add[E](e:E):Int, you would just modify these two lines:
case Inl(h) => addH.add(h)
case Inr(t) => addT.add(t)

Merge non-null fields in two case classes

I'm trying to modify caseclassmerge example from shapeless library to only merge non-null fields.
object mergeSyntax {
implicit class MergeSyntax[T](t: T) {
def merge[U](u: U)(implicit merge: CaseClassMerge[T, U]): T = merge(t, u)
}
}
trait CaseClassMerge[T, U] {
def apply(t: T, u: U): T
}
object CaseClassMerge {
import ops.record.Merger
def apply[T, U](implicit merge: CaseClassMerge[T, U]): CaseClassMerge[T, U] = merge
implicit def mkCCMerge[T, U, RT <: HList, RU <: HList]
(implicit
tgen: LabelledGeneric.Aux[T, RT],
ugen: LabelledGeneric.Aux[U, RU],
merger: Merger.Aux[RT, RU, RT]
): CaseClassMerge[T, U] =
new CaseClassMerge[T, U] {
def apply(t: T, u: U): T =
tgen.from(merger(tgen.to(t), ugen.to(u)))
}
}
How to modify the merging logic in a way that only non-null fields in the second argument will be merged into the first argument?
You could add you own implicit merger implementation to the current scope, overriding the standard merger:
object NotNullHListMerger {
import shapeless.labelled._
import shapeless.ops.record.Merger
import shapeless.ops.record.Remover
implicit def notNullHListMerger[K, V, T <: HList, M <: HList, MT <: HList]
(implicit
rm: Remover.Aux[M, K, (V, MT)],
mt: Merger[T, MT]
): Merger.Aux[FieldType[K, V] :: T, M, FieldType[K, V] :: mt.Out] =
new Merger[FieldType[K, V] :: T, M] {
type Out = FieldType[K, V] :: mt.Out
def apply(l: FieldType[K, V] :: T, m: M): Out = {
val (mv, mr) = rm(m)
val up = field[K](mv)
// Replace only if value is not null
val h = Option(up).getOrElse(l.head)
h :: mt(l.tail, mr)
}
}
}
import mergeSyntax._
import NotNullHListMerger._
case class Foo(i: Int, s: String, b: Boolean)
case class Bar(b: Boolean, s: String)
val foo = Foo(23, "foo", true)
val bar = Bar(false, null)
val merged = foo merge bar
assert(merged == Foo(23, "foo", false))

How to create case class from List[String]?

Here is my code
scala> s
res6: String = 2005-05-06 14:58:56 192 45.14.5.238 200 TCP_NC_MISS 1123 496 GET http c4.maxserving.com /gen.js ?site=5835&area=side_ros&group=sidebar&PageID=33364329499 - DIRECT c4.maxserving.com application/x-javascript "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)" PROXIED Web%20Advertisements - 192.16.170.44 SG-HTTP-Service - none -
scala> s.split("\\s")
res7: Array[String] = Array(2005-05-06, 14:58:56, 192, 45.14.5.238, 200, TCP_NC_MISS, 1123, 496, GET, http, c4.maxserving.com, /gen.js, ?site=5835&area=side_ros&group=sidebar&PageID=33364329499, -, DIRECT, c4.maxserving.com, application/x-javascript, "Mozilla/4.0, (compatible;, MSIE, 6.0;, Windows, NT, 5.1;, SV1;, .NET, CLR, 1.1.4322)", PROXIED, Web%20Advertisements, -, 192.16.170.44, SG-HTTP-Service, -, none, -)
scala> case class BlueCoatEvent(date: String,
| time: String,
| timeTaken: String,
| cIp: String,
| scStatus: String,
| sAction: String,
| scBytes: String,
| csBytes: String,
| csMethod: String,
| csUriScheme: String,
| csHost: String,
| csUriPath: String,
| csUriQuery: String,
| csUsername: String,
| sHierarchy: String,
| sSupplierName: String,
| rsContentType: String,
| csUserAgent: String,
| scFilterResult: String,
| scFilterCategory: String,
| xVirusId: String,
| sIp: String,
| sSiteName: String,
| xVirusDetails: String,
| xIcapErrorCode: String,
| xIcapErrorDetails: String)
defined class BlueCoatEvent
scala>
How do I create blueCoatEvent from s.split("\\s")?
See answer in Instantiating a case class from a list of parameters
Introduce meaningsful sub-types/case classes and compose BlueCoatEvent with them.
The ugly horrible way is this:
scala> case class A(x: String, y: String, z: String)
defined class A
scala> def toTuple[A <: Object](as:List[A]):Product = {
| val tupleClass = Class.forName("scala.Tuple" + as.size)
| tupleClass.getConstructors.apply(0).newInstance(as:_*).asInstanceOf[Product]
| }
toTuple: [A <: Object](as: List[A])Product
scala> val l = List("a", "b", "c")
l: List[String] = List(a, b, c)
scala> val t3 = toTuple(l).asInstanceOf[Tuple3[String, String, String]]
t3: (String, String, String) = (a,b,c)
scala> val f = A.tupled
f: ((String, String, String)) => A = <function1>
scala> f(t3)
res0: A = A(a,b,c)
You can use any way you want to convert from collection to TupleN:
Convert a Scala list to a tuple?
Is there way to create tuple from list(without codegeneration)?
I picked toTuple from there.
This is unsafe, ugly and does not save you much. You can resort to code generation, reflection or macros to get something more helpful.
Another option:
Based on this idea Applying an argument list to curried function using foldLeft in Scala we can use A.curried with HList to produce a cleaner solution:
object CaseClassFromList extends App {
sealed trait HList
final case class HCons[H, T <: HList](head : H, tail : T) extends HList {
def ::[H1](h : H1) = HCons(h, this)
override def toString = head+" :: "+tail.toString
}
trait HNil extends HList {
def ::[H1](h : H1) = HCons(h, this)
override def toString = "HNil"
}
case object HNil extends HNil
type ::[H, T <: HList] = HCons[H, T]
trait FoldCurry[L <: HList, F, Out] {
def apply(l : L, f : F) : Out
}
// Base case for HLists of length one
implicit def foldCurry1[H, Out] = new FoldCurry[H :: HNil, H => Out, Out] {
def apply(l : H :: HNil, f : H => Out) = f(l.head)
}
// Case for HLists of length n+1
implicit def foldCurry2[H, T <: HList, FT, Out]
(implicit fct : FoldCurry[T, FT, Out]) = new FoldCurry[H :: T, H => FT, Out] {
def apply(l : H :: T, f : H => FT) = fct(l.tail, f(l.head))
}
// Public interface ... implemented in terms of type class and instances above
def foldCurry[L <: HList, F, Out](l : L, f : F)
(implicit fc : FoldCurry[L, F, Out]) : Out = fc(l, f)
case class A(x: String, y: String, z: String)
//val l = List("a", "b", "c")
val lh = "a" :: "b" :: "c" :: HNil
val newA = foldCurry(lh, A.curried)
println(newA)
}
The problem again is that you can't get away from spelling out the types whether those are Tuple or HList unless you go unsafe way or some sort of code generation.
A different approach by defining a Map[String,String] over the values of an event; let
case class EventMap( data: Map[String,String])
and
def fields(cc: Product) = cc.getClass.getDeclaredFields.map(_.getName)
Then from
EventMap( fields(BlueCoatEvent) zip s.split("\\s") toMap )
we can fetch values properties for a given stringisized BlueCoatEvent.