HList foldLeft with tuple as zero - scala

I'm trying to foldLeft on a HList with an accumulator of type (HL, Int), where HL is a HList. The program below does not compile. However, if I switch to a simpler accumulator of type HL (by just switching the commented lines with the ones above), it compiles and it works.
Wrapping an HList in a tuple breaks the implicit resolution for the leftFolder. What am I missing?
package foo.bar
import shapeless.{:+:, ::, CNil, Coproduct, Generic, HList, HNil, Lazy, Poly2}
import shapeless.ops.hlist.{LeftFolder, Reverse}
object StackOverflow extends App {
trait MyTypeclass[T] {
def doSomething(t: T): (T, Int)
}
implicit lazy val stringInstance: MyTypeclass[String] = (t: String) => (t, 0)
implicit val hnilInstance: MyTypeclass[HNil] = (t: HNil) => (t, 0)
implicit def hlistInstance[H, T <: HList](
implicit
head: Lazy[MyTypeclass[H]],
tail: MyTypeclass[T]
): MyTypeclass[H :: T] =
(ht: H :: T) =>
ht match {
case h :: t =>
val (hres, hint) = head.value.doSomething(h)
val (tres, tint) = tail.doSomething(t)
(hres :: tres, hint + tint)
}
implicit val cnilInstance: MyTypeclass[CNil] = (t: CNil) => ???
implicit def coproductInstance[L, R <: Coproduct](
implicit
head: Lazy[MyTypeclass[L]],
tail: MyTypeclass[R]
): MyTypeclass[L :+: R] = (lr: L :+: R) => ???
object leftFolder extends Poly2 {
implicit def caseAtSimple[F, HL <: HList]: Case.Aux[HL, F, F :: HL] =
at {
case (acc, f) => f :: acc
}
implicit def caseAtComplex[F, HL <: HList]: Case.Aux[(HL, Int), F, (F :: HL, Int)] =
at {
case ((acc, i), f) => (f :: acc, i)
}
}
implicit def genericInstance[T, HL <: HList, LL <: HList](
implicit
gen: Generic.Aux[T, HL],
myTypeclass: Lazy[MyTypeclass[HL]],
// folder: LeftFolder.Aux[HL, HNil, leftFolder.type, LL],
folder: LeftFolder.Aux[HL, (HNil, Int), leftFolder.type, (LL, Int)],
reverse: Reverse.Aux[LL, HL]
): MyTypeclass[T] = (t: T) => {
val generic = gen.to(t)
val (transformed, idx) = myTypeclass.value.doSomething(generic)
// val ll = transformed.foldLeft(HNil: HNil)(leftFolder)
val (ll, _) = transformed.foldLeft((HNil: HNil, 0))(leftFolder)
val reversed = reverse(ll)
(gen.from(reversed), idx)
}
def doSomething[T](t: T)(implicit myTypeclass: MyTypeclass[T]): T = myTypeclass.doSomething(t)._1
case class Foo(
str1: String,
str2: String
)
val original = Foo("Hello World!", "Hello there!")
val result = doSomething(original)
println(result == original)
}

You want implicits to do too much work in a single step.
Try to add one more type parameter Out
implicit def genericInstance[T, HL <: HList, Out, LL <: HList](
implicit
gen: Generic.Aux[T, HL],
myTypeclass: Lazy[MyTypeclass[HL]],
//folder: LeftFolder.Aux[HL, (HNil, Int), leftFolder.type, (LL, Int)],
folder: LeftFolder.Aux[HL, (HNil, Int), leftFolder.type, Out],
ev: Out <:< (LL, Int), // added
reverse: Reverse.Aux[LL, HL]
): MyTypeclass[T] = (t: T) => {
val generic = gen.to(t)
val (transformed, idx) = myTypeclass.value.doSomething(generic)
//val (ll, _) = transformed.foldLeft((HNil: HNil, 0))(leftFolder)
val (ll, _) = ev(transformed.foldLeft((HNil: HNil, 0))(leftFolder))
val reversed = reverse(ll)
(gen.from(reversed), idx)
}
Read about over-constrained implicits:
https://books.underscore.io/shapeless-guide/shapeless-guide.html#sec:type-level-programming:chaining (4.3 Chaining dependent functions)
Scala shapeless Generic.Aux implicit parameter not found in unapply
Extract FieldType key and value from HList
How to implicitly figure out the type at the head of a shapeless HList
How to infer inner type of Shapeless record value with unary type constructor?

Related

Shapeless and annotations

I would like to have some function applied to fields in a case class, that are annotated with MyAnnotation. The idea is to transform type T into its generic representation, extract annotations, zip, fold right (or left) to reconstruct a generic representation and finally get back to type T. I followed the answer provided here and this gist.
I'm using scala 2.11.12 and shapeless 2.3.3.
Hereafter is my code:
import shapeless._
import shapeless.ops.hlist._
case class MyAnnotation(func: String) extends scala.annotation.StaticAnnotation
trait Modifier[T] {
def modify(t: T): T
}
object Modifier {
def apply[A: Modifier]: Modifier[A] = implicitly[Modifier[A]]
def create[T](func: T => T): Modifier[T] = new Modifier[T] { override def modify(t: T): T = func(t) }
private def id[T](t: T) = t
implicit val stringModifier: Modifier[String] = create(id)
implicit val booleanModifier: Modifier[Boolean] = create(id)
implicit val byteModifier: Modifier[Byte] = create(id)
implicit val charModifier: Modifier[Char] = create(id)
implicit val doubleModifier: Modifier[Double] = create(id)
implicit val floatModifier: Modifier[Float] = create(id)
implicit val intModifier: Modifier[Int] = create(id)
implicit val longModifier: Modifier[Long] = create(id)
implicit val shortModifier: Modifier[Short] = create(id)
implicit val hnilModifier: Modifier[HNil] = create(id)
implicit def hlistModifier[H, T <: HList, AL <: HList](
implicit
hser: Lazy[Modifier[H]],
tser: Modifier[T]
): Modifier[H :: T] = new Modifier[H :: T] {
override def modify(ht: H :: T): H :: T = {
ht match {
case h :: t =>
hser.value.modify(h) :: tser.modify(t)
}
}
}
implicit val cnilModifier: Modifier[CNil] = create(id)
implicit def coproductModifier[L, R <: Coproduct](
implicit
lser: Lazy[Modifier[L]],
rser: Modifier[R]
): Modifier[L :+: R] = new Modifier[L :+: R] {
override def modify(t: L :+: R): L :+: R = t match {
case Inl(l) => Inl(lser.value.modify(l))
case Inr(r) => Inr(rser.modify(r))
}
}
object Collector extends Poly2 {
implicit def myCase[ACC <: HList, E] = at[(E, Option[MyAnnotation]), ACC] {
case ((e, None), acc) => e :: acc
case ((e, Some(MyAnnotation(func))), acc) => {
println(func)
e :: acc
}
}
}
implicit def genericModifier[T, HL <: HList, AL <: HList, ZL <: HList](
implicit
gen: Generic.Aux[T, HL],
ser: Lazy[Modifier[HL]],
annots: Annotations.Aux[MyAnnotation, T, AL],
zip: Zip.Aux[HL :: AL :: HNil, ZL],
rightFolder: RightFolder[ZL, HNil.type, Collector.type]
): Modifier[T] = new Modifier[T] {
override def modify(t: T): T = {
val generic = gen.to(t)
println(generic)
val annotations = annots()
println(annotations)
val zipped = zip(generic :: annotations :: HNil)
println(zipped)
val modified = zipped.foldRight(HNil)(Collector)
println(modified)
val typed = gen.from(generic) // temporary
typed
}
}
}
The code above compiles. However, when instanciating a Modifier in a test:
case class Test(a: String, #MyAnnotation("sha1") b: String)
val test = Test("A", "B")
val modifier: Modifier[Test] = implicitly
the test file does not compile and give the following error:
[error] ambiguous implicit values:
[error] both value StringCanBuildFrom in object Predef of type =>
scala.collection.generic.CanBuildFrom[String,Char,String]
[error] and method $conforms in object Predef of type [A]=> <:<[A,A]
[error] match expected type T
[error] val ser1: Modifier[Test] = implicitly
The problem seems to come from the right folder definition: when removing rightFolder from the list of implicits in genericModifier, then it works:
implicit def genericModifier[T, HL <: HList, AL <: HList, ZL <: HList](
implicit
gen: Generic.Aux[T, HL],
ser: Lazy[Modifier[HL]],
annots: Annotations.Aux[MyAnnotation, T, AL],
zip: Zip.Aux[HL :: AL :: HNil, ZL]/*,
rightFolder: RightFolder[ZL, HNil.type, Collector.type]*/
): Modifier[T] = new Modifier[T] {
override def modify(t: T): T = {
val generic = gen.to(t)
println(generic)
val annotations = annots()
println(annotations)
val zipped = zip(generic :: annotations :: HNil)
println(zipped)
/*val modified = zipped.foldRight(HNil)(Collector)
println(modified)*/
val typed = gen.from(generic) // temporary
typed
}
}
What is wrong?
There are several mistakes in your code:
defining Poly just for Option is too rough (pattern matching is performed at runtime and compiler should know definitions for Some and None at compile time)
HNil should be instead of HNil.type and HNil : HNil instead of HNil (types HNil and HNil.type are different)
compiler doesn't know that RightFolder actually returns the original HList type, so you should use RightFolder.Aux type.
Correct code is
import shapeless.ops.hlist.{RightFolder, Zip}
import shapeless.{::, Annotations, Generic, HList, HNil, Lazy, Poly2}
import scala.annotation.StaticAnnotation
object App {
case class MyAnnotation(func: String) extends StaticAnnotation
object Collector extends Poly2 {
// implicit def myCase[ACC <: HList, E] = at[(E, Option[PII]), ACC] {
// case ((e, None), acc) => e :: acc
// case ((e, Some(MyAnnotation(func))), acc) => {
// println(func)
// e :: acc
// }
// }
implicit def someCase[ACC <: HList, E]: Case.Aux[(E, Some[MyAnnotation]), ACC, E :: ACC] = at {
case ((e, Some(MyAnnotation(func))), acc) =>
println(func)
e :: acc
}
implicit def noneCase[ACC <: HList, E]: Case.Aux[(E, None.type), ACC, E :: ACC] = at {
case ((e, None), acc) => e :: acc
}
}
trait Modifier[T] {
def modify(t: T): T
}
implicit def hListModifier[HL <: HList]: Modifier[HL] = identity(_)
// added as an example, you should replace this with your Modifier for HList
implicit def genericModifier[T, HL <: HList, AL <: HList, ZL <: HList](implicit
gen: Generic.Aux[T, HL],
ser: Lazy[Modifier[HL]],
annots: Annotations.Aux[MyAnnotation, T, AL],
zip: Zip.Aux[HL :: AL :: HNil, ZL],
rightFolder: RightFolder.Aux[ZL, HNil/*.type*/, Collector.type, HL /*added*/]
): Modifier[T] = new Modifier[T] {
override def modify(t: T): T = {
val generic = gen.to(t)
println(generic)
val annotations = annots()
println(annotations)
val zipped = zip(generic :: annotations :: HNil)
println(zipped)
val modified = zipped.foldRight(HNil : HNil /*added*/)(Collector)
println(modified)
val typed = gen.from(modified)
typed
}
}
case class Test(a: String, #MyAnnotation("sha1") b: String)
val test = Test("A", "B")
val modifier: Modifier[Test] = implicitly[Modifier[Test]]
def main(args: Array[String]): Unit = {
val test1 = modifier.modify(test) // prints "sha1"
println(test1) // Test(A,B)
}
}

Create "enriched" type from case class with Shapeless in Scala

I have this example code:
import java.util.UUID
import shapeless.LabelledGeneric
import shapeless.record._
import shapeless.syntax.singleton._
object LabelTest extends App {
case class IncomingThing(name: String, age: Int)
case class DatabaseIncomingThing(name: String, age: Int, id: UUID)
val genIncoming = LabelledGeneric[IncomingThing]
val genDatabase = LabelledGeneric[DatabaseIncomingThing]
val thing = IncomingThing("John", 42)
val structuralVersionOfIncomingThing = genIncoming.to(thing)
val updated = genDatabase.from(structuralVersionOfIncomingThing + ('id ->> UUID.randomUUID()))
println(updated) // DatabaseIncomingThing(John,42,a45081f2-4ed5-4d2b-8fd9-4d8986875ed7)
}
Which is nice because I don't have to write the boilerplate that copies over all the fields from IncomingThing to DatabaseIncomingThing. However, I would love not having to maintain both those types since there is a very clear relationship between the two (one has id, the other doesn't).
Is there a way to create a type from a given case class by either adding or removing one field?
I imagine something like
type IncomingThing = withoutField[DatabaseIncomingThing]('id)
Or something to that effect.
Instead of DatabaseIncomingThing
val updated: DatabaseIncomingThing =
genDatabase.from(structuralVersionOfIncomingThing + ('id ->> UUID.randomUUID()))
you can work with raw HList
val updated1: FieldType[Witness.`'name`.T, String] :: FieldType[Witness.`'age`.T, Int] :: FieldType[Witness.`'id`.T, UUID] :: HNil =
structuralVersionOfIncomingThing + ('id ->> UUID.randomUUID())
val updated2: FieldType[Witness.`'name`.T, String] :: FieldType[Witness.`'age`.T, Int] :: HNil =
updated1 - 'id
On type level
implicitly[Remover.Aux[FieldType[Witness.`'name`.T, String] :: FieldType[Witness.`'age`.T, Int] :: FieldType[Witness.`'id`.T, UUID] :: HNil,
Witness.`'id`.T,
(UUID, FieldType[Witness.`'name`.T, String] :: FieldType[Witness.`'age`.T, Int] :: HNil)]]
implicitly[Updater.Aux[
FieldType[Witness.`'name`.T, String] :: FieldType[Witness.`'age`.T, Int] :: HNil,
FieldType[Witness.`'id`.T, UUID],
FieldType[Witness.`'name`.T, String] :: FieldType[Witness.`'age`.T, Int] :: FieldType[Witness.`'id`.T, UUID] :: HNil]]
You can create your type class
trait WithoutField[A, K] {
type Out <: HList
}
object WithoutField {
type Aux[A, K, Out0 <: HList] = WithoutField[A, K] { type Out = Out0 }
def instance[A, K, Out0 <: HList]: Aux[A, K, Out0] = new WithoutField[A, K] { type Out = Out0 }
implicit def mkWithoutField[A, L <: HList, K, T, L1 <: HList](implicit
labelledGeneric: LabelledGeneric.Aux[A, L],
remover: Remover.Aux[L, K, (T, L1)]): Aux[A, K, L1] = instance
}
and use it
def foo[Out <: HList](implicit withoutField: WithoutField.Aux[DatabaseIncomingThing, Witness.`'id`.T, Out]) = {
// now you can use type Out inside
type IncomingThing = Out
???
}

HList transformation Out type

I have created a wrapper on top on HList that can append an HLists. It is defined as follows:
class HListWrapper[L <: HList](val hl: L) {
def append[V, Out <: HList](k: Witness, v: V)(implicit updater: Updater.Aux[L, FieldType[k.T, V], Out],
lk: LacksKey[L, k.T]): HListWrapper[Out] = {
new HListWrapper(updater(hl, field[k.T](v)))
}
}
object HListWrapper {
def apply[P <: Product, L <: HList](p: P)(implicit gen: LabelledGeneric.Aux[P, L]) =
new HListWrapper(gen.to(p))
}
It used like this:
case class CC(i: Int, ii: Int)
val cc = CC(100, 1000)
val hl = HListWrapper(cc)
hl.append('iii, 10000)
But when I try to put the HListWrapper inside another function to capture the type of Out, the compiler cannot seem to resolve the final type of the transformation (fails with type mismatch error):
def cctohlist[Out <: HList]: CC => HListWrapper[Out] = {
m => {
val hl = HListWrapper(m)
hl.append('iii, 10000)
}
}
The primary reason to create the cctohlist method is to get the type of the HList after append. Is this possible to achieve?
The following code works:
def cctohlist: CC => HListWrapper[Record.`'i -> Int, 'ii -> Int, 'iii -> Int`.T] = {
m => {
val hl = HListWrapper(m)
hl.append('iii, 10000)
}
}
When you write
def cctohlist[Out <: HList]: CC => HListWrapper[Out] = ???
this means I can apply cctohlist[Int :: String :: Boolean :: HNil] and have CC => HListWrapper[Int :: String :: Boolean :: HNil] or I can apply cctohlist[AnyVal :: AnyRef :: Any :: HNil] and have CC => HListWrapper[AnyVal :: AnyRef :: Any :: HNil] etc. This is obviously not the case.
This is generic one:
def cctohlist[P <: Product, L <: HList, Out <: HList](m: P)(implicit
gen: LabelledGeneric.Aux[P, L],
updater: Updater.Aux[L, FieldType[Witness.`'iii`.T, Int], Out],
lk: LacksKey[L, Witness.`'iii`.T]
): HListWrapper[Out] = {
val hl = HListWrapper(m)
hl.append('iii, 10000)
}
cctohlist(cc)

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)