/* * Copyright (c) 2015 Miles Sabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package derivation import shapeless._ import scala.util.Try object repr extends ShowReprDefns { sealed trait Animal case class Cat(name: String, fish: Int) extends Animal case class Dog(name: String, bones: Int) extends Animal val felix: Animal = Cat("Felix", 1) val tigger: Animal = Dog("Tigger", 2) } object writer { trait Writer[T] { def write(t: T): String } object Writer { def write[T](t: T)(implicit writer: Writer[T]): String = { writer.write(t) } implicit val intWriter = new Writer[Int] { def write(t: Int): String = s"$t : Int" } implicit val stringWriter = new Writer[String] { def write(t: String): String = s"$t : String" } implicit val booleanWriter = new Writer[Boolean] { def write(t: Boolean): String = s"$t : Boolean" } // Base case for products implicit val writerHNil: Writer[HNil] = new Writer[HNil] { def write(t: HNil): String = "" } // induction for products implicit def writerHCons[H, T <: HList](implicit writerH: Lazy[Writer[H]], writerT: Lazy[Writer[T]]): Writer[H :: T] = new Writer[H :: T] { def write(t: H :: T): String = writerH.value.write(t.head) + ", " + writerT.value.write(t.tail) } implicit val writerCNil: Writer[CNil] = new Writer[CNil] { def write(t: CNil): String = "" } implicit def writerCoproduct[H, T <: Coproduct](implicit writerH: Lazy[Writer[H]], writerT: Lazy[Writer[T]]): Writer[H :+: T] = { new Writer[H :+: T] { override def write(t: :+:[H, T]): String = t match { case Inl(l) => writerH.value.write(l) case Inr(r) => writerT.value.write(r) } } } implicit def writerGeneric[T, R](implicit gen: Generic.Aux[T, R], writerRepr: Lazy[Writer[R]]): Writer[T] = new Writer[T] { def write(t: T): String = writerRepr.value.write(gen.to(t)) } implicit class RichAnimal(val animal: Animal) extends AnyVal { def write(implicit writer: Writer[Animal]) = writer.write(animal) } } sealed trait Animal case class Cat(name: String, fish: Int, badAttitude: Boolean) extends Animal case class Dog(name: String, bones: Int) extends Animal case class Cow(steak:String) extends Animal } object reader { trait Reader[T] { def read(in: String): T } object Reader { def read[T](s: String)(implicit reader: Reader[T]): T = { reader.read(s) } implicit val intReader: Reader[Int] = new Reader[Int] { def read(in: String): Int = in.toInt } implicit val stringReader: Reader[String] = new Reader[String] { def read(in: String): String = in } implicit val booleanReader: Reader[Boolean] = new Reader[Boolean] { def read(in: String): Boolean = in.toBoolean } implicit val hnilReader: Reader[HNil] = new Reader[HNil] { def read(in: String): HNil = HNil } import scala.collection.immutable.{:: => :&:} implicit def productReader[H, T <: HList](implicit hReader: Lazy[Reader[H]], tReader: Lazy[Reader[T]]): Reader[H :: T] = new Reader[H :: T] { def read(in: String): H :: T = { val (head :&: tail) = in.split(",").toList hReader.value.read(head.takeWhile(_ != ':').trim) :: tReader.value.read(tail.mkString(",")) } } implicit val cnilReader: Reader[CNil] = new Reader[CNil] { def read(in: String): CNil = null } implicit def coproductReader[H, T <: Coproduct](implicit hReader: Lazy[Reader[H]], tReader: Lazy[Reader[T]]): Reader[H:+:T] = new Reader[H:+:T] { def read(in: String): H :+: T = { Try(Inl[H,T](hReader.value.read(in))).recover { case _ => Inr[H,T](tReader.value.read(in)) }.get } } implicit def readerGeneric[T, R](implicit gen: Generic.Aux[T, R], readerRepr: Lazy[Reader[R]]): Reader[T] = new Reader[T] { def read(str: String): T = gen.from(readerRepr.value.read(str)) } } } object equalManual { // Cats/Algebra Eq trait Eq[T] { def eqv(x: T, y: T): Boolean } object Eq { implicit val eqInt: Eq[Int] = new Eq[Int] { def eqv(x: Int, y: Int): Boolean = x == y } implicit val eqString: Eq[String] = new Eq[String] { def eqv(x: String, y: String): Boolean = x == y } } implicit class EqOps[T](x: T)(implicit eqT: Eq[T]) { def ===(y: T): Boolean = eqT.eqv(x, y) } sealed trait Animal case class Cat(name: String, fish: Int) extends Animal case class Dog(name: String, bones: Int) extends Animal object Animal { implicit val eqAnimal: Eq[Animal] = new Eq[Animal] { def eqv(x: Animal, y: Animal): Boolean = (x, y) match { case (x: Cat, y: Cat) => x === y case (x: Dog, y: Dog) => x === y case _ => false } } } object Cat { implicit val eqCat: Eq[Cat] = new Eq[Cat] { def eqv(x: Cat, y: Cat): Boolean = x.name === y.name && x.fish === y.fish } } object Dog { implicit val eqDog: Eq[Dog] = new Eq[Dog] { def eqv(x: Dog, y: Dog): Boolean = x.name === y.name && x.bones === y.bones } } val felix: Animal = Cat("Felix", 1) val tigger: Animal = Dog("Tigger", 2) } object equal { // Cats/Algebra Eq trait Eq[T] { def eqv(x: T, y: T): Boolean } object Eq { implicit val eqInt: Eq[Int] = new Eq[Int] { def eqv(x: Int, y: Int): Boolean = x == y } implicit val eqString: Eq[String] = new Eq[String] { def eqv(x: String, y: String): Boolean = x == y } implicit def eqGeneric[T, R] (implicit gen: Generic.Aux[T, R], eqRepr: Lazy[Eq[R]] ): Eq[T] = new Eq[T] { def eqv(x: T, y: T): Boolean = eqRepr.value.eqv(gen.to(x), gen.to(y)) } // Base case for products implicit val eqHNil: Eq[HNil] = new Eq[HNil] { def eqv(x: HNil, y: HNil): Boolean = true } // Induction step for products implicit def eqHCons[H, T <: HList] (implicit eqH: Lazy[Eq[H]], eqT: Lazy[Eq[T]] ): Eq[H :: T] = new Eq[H :: T] { def eqv(x: H :: T, y: H :: T): Boolean = eqH.value.eqv(x.head, y.head) && eqT.value.eqv(x.tail, y.tail) } // Base case for coproducts implicit val eqCNil: Eq[CNil] = new Eq[CNil] { def eqv(x: CNil, y: CNil): Boolean = true } // Induction step for products implicit def eqCNCons[H, T <: Coproduct] (implicit eqH: Lazy[Eq[H]], eqT: Lazy[Eq[T]] ): Eq[H :+: T] = new Eq[H :+: T] { def eqv(x: H :+: T, y: H :+: T): Boolean = (x, y) match { case (Inl(xh), Inl(yh)) => eqH.value.eqv(xh, yh) case (Inr(xt), Inr(yt)) => eqT.value.eqv(xt, yt) case _ => false } } } implicit class EqOps[T](x: T)(implicit eqT: Eq[T]) { def ===(y: T): Boolean = eqT.eqv(x, y) } sealed trait Animal case class Cat(name: String, fish: Int) extends Animal case class Dog(name: String, bones: Int) extends Animal val felix: Animal = Cat("Felix", 1) val tigger: Animal = Dog("Tigger", 2) } object ordering { // Cats/Algebra Order trait Order[T] { def compare(x: T, y: T): Int } object Order { implicit val ordInt: Order[Int] = new Order[Int] { def compare(x: Int, y: Int): Int = x-y } implicit val ordString: Order[String] = new Order[String] { def compare(x: String, y: String): Int = x compare y } implicit def ordGeneric[T, R] (implicit gen: Generic.Aux[T, R], ordRepr: Lazy[Order[R]] ): Order[T] = new Order[T] { def compare(x: T, y: T): Int = ordRepr.value.compare(gen.to(x), gen.to(y)) } // Base case for products implicit val ordHNil: Order[HNil] = new Order[HNil] { def compare(x: HNil, y: HNil): Int = 0 } // Induction step for products implicit def ordHCons[H, T <: HList] (implicit ordH: Lazy[Order[H]], ordT: Lazy[Order[T]] ): Order[H :: T] = new Order[H :: T] { def compare(x: H :: T, y: H :: T): Int = { val cmpH = ordH.value.compare(x.head, y.head) if(cmpH != 0) cmpH else ordT.value.compare(x.tail, y.tail) } } // Base case for coproducts implicit val ordCNil: Order[CNil] = new Order[CNil] { def compare(x: CNil, y: CNil): Int = 0 } // Induction step for products implicit def ordCNCons[H, T <: Coproduct] (implicit ordH: Lazy[Order[H]], ordT: Lazy[Order[T]] ): Order[H :+: T] = new Order[H :+: T] { def compare(x: H :+: T, y: H :+: T): Int = (x, y) match { case (Inl(xh), Inl(yh)) => ordH.value.compare(xh, yh) case (Inl(xh), Inr(yt)) => 1 case (Inr(xh), Inl(yt)) => -1 case (Inr(xt), Inr(yt)) => ordT.value.compare(xt, yt) } } } implicit class OrderOps[T](x: T)(implicit ordT: Order[T]) { def compare(y: T): Int = ordT.compare(x, y) } sealed trait Animal case class Cat(name: String, fish: Int) extends Animal case class Dog(name: String, bones: Int) extends Animal val felix: Animal = Cat("Felix", 1) val tigger: Animal = Dog("Tigger", 2) } object monoid { // Cats/Algebra monoid trait Monoid[T] { def empty: T def combine(x: T, y: T): T } object Monoid { def apply[T](implicit monT: Monoid[T]) = monT implicit val booleanMonoid: Monoid[Boolean] = new Monoid[Boolean] { val empty = false def combine(x: Boolean, y: Boolean) = x || y } implicit val intMonoid: Monoid[Int] = new Monoid[Int] { val empty = 0 def combine(x: Int, y: Int) = x+y } implicit val doubleMonoid: Monoid[Double] = new Monoid[Double] { val empty = 0.0 def combine(x: Double, y: Double) = x+y } implicit val stringMonoid: Monoid[String] = new Monoid[String] { val empty = "" def combine(x: String, y: String) = x+y } implicit def monGeneric[T, R] (implicit gen: Generic.Aux[T, R], monRepr: Lazy[Monoid[R]] ): Monoid[T] = new Monoid[T] { val empty = gen.from(monRepr.value.empty) def combine(x: T, y: T): T = gen.from(monRepr.value.combine(gen.to(x), gen.to(y))) } // Base case for products implicit val monHNil: Monoid[HNil] = new Monoid[HNil] { val empty = HNil def combine(x: HNil, y: HNil) = HNil } // Induction step for products implicit def monHCons[H, T <: HList] (implicit monH: Lazy[Monoid[H]], monT: Lazy[Monoid[T]] ): Monoid[H :: T] = new Monoid[H :: T] { val empty = monH.value.empty :: monT.value.empty def combine(x: H :: T, y: H :: T) = monH.value.combine(x.head, y.head) :: monT.value.combine(x.tail, y.tail) } } implicit class MonoidOps[T](x: T)(implicit monT: Monoid[T]) { def combine(y: T): T = monT.combine(x, y) } case class Cat(name: String, fish: Int) case class Dog(name: String, bones: Int) val felix = Cat("Felix", 1) val tigger = Dog("Tigger", 2) } object show { // Show using low-level infrastructure ... import labelled._ trait Show[T] { def show(t: T): String } object Show { implicit val showString: Show[String] = new Show[String] { def show(t: String) = t } implicit val showInt: Show[Int] = new Show[Int] { def show(t: Int) = t.toString } implicit def showList[A](implicit showA: Show[A]): Show[List[A]] = new Show[List[A]] { def show(t: List[A]) = t.map(showA.show).mkString("List(", ", ", ")") } implicit def showGeneric[F, G](implicit gen: LabelledGeneric.Aux[F, G], sg: Lazy[Show[G]]): Show[F] = new Show[F] { def show(f: F) = sg.value.show(gen.to(f)) } implicit def showHNil: Show[HNil] = new Show[HNil] { def show(p: HNil): String = "" } implicit def showHCons[K <: Symbol, V, T <: HList] (implicit key: Witness.Aux[K], sv: Lazy[Show[V]], st: Lazy[Show[T]] ): Show[FieldType[K, V] :: T] = new Show[FieldType[K, V] :: T] { def show(p: FieldType[K, V] :: T): String = { val head = s"${key.value.name} = ${sv.value.show(p.head)}" val tail = st.value.show(p.tail) if(tail.isEmpty) head else s"$head, $tail" } } implicit def showCNil: Show[CNil] = new Show[CNil] { def show(p: CNil): String = "" } implicit def showCCons[K <: Symbol, V, T <: Coproduct] (implicit key: Witness.Aux[K], sv: Lazy[Show[V]], st: Lazy[Show[T]] ): Show[FieldType[K, V] :+: T] = new Show[FieldType[K, V] :+: T] { def show(c: FieldType[K, V] :+: T): String = c match { case Inl(l) => s"${key.value.name}(${sv.value.show(l)})" case Inr(r) => st.value.show(r) } } } implicit class ShowOps[T](x: T)(implicit showT: Show[T]) { def show: String = showT.show(x) } sealed trait Animal case class Cat(name: String, fish: Int) extends Animal case class Dog(name: String, bones: Int) extends Animal val felix = Cat("Felix", 1) val tigger = Dog("Tigger", 2) } object show2 { // Show using TypeClass trait Show[T] { def show(t: T): String } object Show extends LabelledTypeClassCompanion[Show] { implicit def stringShow: Show[String] = new Show[String] { def show(t: String) = t } implicit def intShow: Show[Int] = new Show[Int] { def show(n: Int) = n.toString } object typeClass extends LabelledTypeClass[Show] { def emptyProduct = new Show[HNil] { def show(t: HNil) = "" } def product[F, T <: HList](name: String, sh: Show[F], st: Show[T]) = new Show[F :: T] { def show(ft: F :: T) = { val head = sh.show(ft.head) val tail = st.show(ft.tail) if (tail.isEmpty) s"$name = $head" else s"$name = $head, $tail" } } def emptyCoproduct = new Show[CNil] { def show(t: CNil) = "" } def coproduct[L, R <: Coproduct](name: String, sl: => Show[L], sr: => Show[R]) = new Show[L :+: R] { def show(lr: L :+: R) = lr match { case Inl(l) => s"$name(${sl.show(l)})" case Inr(r) => s"${sr.show(r)}" } } def project[F, G](instance: => Show[G], to: F => G, from: G => F) = new Show[F] { def show(f: F) = instance.show(to(f)) } } } implicit class ShowOps[T](x: T)(implicit showT: Show[T]) { def show: String = showT.show(x) } sealed trait Animal case class Cat(name: String, fish: Int) extends Animal case class Dog(name: String, bones: Int) extends Animal val felix = Cat("Felix", 1) val tigger = Dog("Tigger", 2) } object functor { trait Functor[F[_]] { def map[A, B](fa: F[A])(f: A => B): F[B] } object Functor extends Functor0 { def apply[F[_]](implicit f: Lazy[Functor[F]]): Functor[F] = f.value implicit val idFunctor: Functor[Id] = new Functor[Id] { def map[A, B](a: A)(f: A => B): B = f(a) } // Induction step for products implicit def hcons[F[_]](implicit ihc: IsHCons1[F, Functor, Functor]): Functor[F] = new Functor[F] { def map[A, B](fa: F[A])(f: A => B): F[B] = { val (hd, tl) = ihc.unpack(fa) ihc.pack((ihc.fh.map(hd)(f), ihc.ft.map(tl)(f))) } } // Induction step for coproducts implicit def ccons[F[_]](implicit icc: IsCCons1[F, Functor, Functor]): Functor[F] = new Functor[F] { def map[A, B](fa: F[A])(f: A => B): F[B] = icc.pack(icc.unpack(fa).fold(hd => Left(icc.fh.map(hd)(f)), tl => Right(icc.ft.map(tl)(f)))) } implicit def generic[F[_]](implicit gen: Generic1[F, Functor]): Functor[F] = new Functor[F] { def map[A, B](fa: F[A])(f: A => B): F[B] = gen.from(gen.fr.map(gen.to(fa))(f)) } } trait Functor0 { implicit def constFunctor[T]: Functor[Const[T]#λ] = new Functor[Const[T]#λ] { def map[A, B](t: T)(f: A => B): T = t } } implicit class FunctorOps[F[_], A](fa: F[A])(implicit F: Functor[F]) { def map[B](f: A => B): F[B] = F.map(fa)(f) } sealed trait Tree[T] case class Leaf[T](t: T) extends Tree[T] case class Node[T](l: Tree[T], r: Tree[T]) extends Tree[T] val tree = Node( Leaf("quux"), Node( Leaf("foo"), Leaf("wibble") ) ) } object foldable { import scala.util.control.TailCalls._ trait Foldable[F[_]] { def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B } object Foldable { implicit def apply[F[_]](implicit fr: Lazy[FoldableRec[F]]): Foldable[F] = new Foldable[F] { def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B = fr.value.foldLeft(fa, b)(f).result } } trait FoldableRec[F[_]] { def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): TailRec[B] } object FoldableRec extends FoldableRec0 { def apply[F[_]](implicit F: Lazy[FoldableRec[F]]): FoldableRec[F] = F.value implicit val idFoldableRec: FoldableRec[Id] = new FoldableRec[Id] { def foldLeft[A, B](fa: A, b: B)(f: (B, A) => B) = done(f(b, fa)) } implicit def hcons[F[_]](implicit F: IsHCons1[F, FoldableRec, FoldableRec]): FoldableRec[F] = new FoldableRec[F] { override def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B) = { val (hd, tl) = F.unpack(fa) for { h <- F.fh.foldLeft(hd, b)(f) t <- F.ft.foldLeft(tl, h)(f) } yield t } } implicit def ccons[F[_]](implicit F: IsCCons1[F, FoldableRec, FoldableRec]): FoldableRec[F] = new FoldableRec[F] { def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B) = F.unpack(fa) match { case Left(l) => tailcall(F.fh.foldLeft(l, b)(f)) case Right(r) => tailcall(F.ft.foldLeft(r, b)(f)) } } implicit def generic[F[_]](implicit F: Generic1[F, FoldableRec]): FoldableRec[F] = new FoldableRec[F] { def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B) = tailcall(F.fr.foldLeft(F.to(fa), b)(f)) } } sealed abstract class FoldableRec0 { implicit def constFoldableRec[T]: FoldableRec[Const[T]#λ] = new FoldableRec[Const[T]#λ] { override def foldLeft[A, B](fa: T, b: B)(f: (B, A) => B) = done(b) } } implicit class FoldableOps[F[_], A](fa: F[A])(implicit F: Foldable[F]) { def foldLeft[B](b: B)(f: (B, A) => B): B = F.foldLeft(fa, b)(f) } sealed trait Tree[T] case class Leaf[T](t: T) extends Tree[T] case class Node[T](l: Tree[T], r: Tree[T]) extends Tree[T] val tree = Node( Leaf("quux"), Node( Leaf("foo"), Leaf("wibble") ) ) sealed abstract class IList[A] final case class ICons[A](head: A, tail: IList[A]) extends IList[A] final case class INil[A]() extends IList[A] val list = (1 to 100000).foldLeft(ICons(0, INil())){ (acc, i) => ICons(i, acc) } } trait ShowReprDefns { def showRepr[T](t: T)(implicit st: ShowRepr[T]): String = st(t) trait ShowRepr[T] { def apply(t: T): String } object ShowRepr extends ShowRepr0 { implicit val hnilShowRepr: ShowRepr[HNil] = new ShowRepr[HNil] { def apply(t: HNil): String = "HNil" } implicit def hconsShowRepr[H, T <: HList] (implicit sh: Lazy[ShowRepr[H]], st: Lazy[ShowRepr[T]] ): ShowRepr[H :: T] = new ShowRepr[H :: T] { def apply(t: H :: T): String = sh.value(t.head)+" :: "+st.value(t.tail) } implicit val cnilShowRepr: ShowRepr[CNil] = new ShowRepr[CNil] { def apply(t: CNil): String = "CNil" } implicit def cconsShowRepr[H, T <: Coproduct] (implicit sh: Lazy[ShowRepr[H]], st: Lazy[ShowRepr[T]] ): ShowRepr[H :+: T] = new ShowRepr[H :+: T] { def apply(t: H :+: T): String = t match { case Inl(l) => "Inl("+sh.value(l)+")" case Inr(r) => "Inr("+st.value(r)+")" } } implicit def genShowRepr[T, R] (implicit gen: Generic.Aux[T, R], sr: Lazy[ShowRepr[R]] ): ShowRepr[T] = new ShowRepr[T] { def apply(t: T): String = sr.value(gen.to(t)) } } trait ShowRepr0 { implicit def default[T]: ShowRepr[T] = new ShowRepr[T] { def apply(t: T): String = t.toString } } }