From 8edaa6e2adbb4aa724a7829be5003fdabb01628a Mon Sep 17 00:00:00 2001 From: Jon Pretty Date: Mon, 30 Oct 2017 12:55:57 +0100 Subject: Derivation working for Show and Eq --- examples/src/main/scala/cats.scala | 19 ------- examples/src/main/scala/eq.scala | 50 ----------------- examples/src/main/scala/typeclasses.scala | 93 +++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 69 deletions(-) delete mode 100644 examples/src/main/scala/cats.scala delete mode 100644 examples/src/main/scala/eq.scala create mode 100644 examples/src/main/scala/typeclasses.scala (limited to 'examples') diff --git a/examples/src/main/scala/cats.scala b/examples/src/main/scala/cats.scala deleted file mode 100644 index 2283ce0..0000000 --- a/examples/src/main/scala/cats.scala +++ /dev/null @@ -1,19 +0,0 @@ -package magnolia.examples - -import scala.collection.immutable.ListMap -import scala.language.experimental.macros - -import cats.Show -import magnolia.{Coderivation, Macros} - -object catsShowDerivation { - - val ShowDerivation = new Coderivation[Show] { - type Return = String - def call[T](show: Show[T], value: T): String = show.show(value) - def construct[T](body: T => String): Show[T] = body(_) - def join(name: String, xs: ListMap[String, String]): String = - xs.map { case (k, v) => s"$k=$v" }.mkString(s"$name(", ", ", ")") - } - -} diff --git a/examples/src/main/scala/eq.scala b/examples/src/main/scala/eq.scala deleted file mode 100644 index 5a4c9fa..0000000 --- a/examples/src/main/scala/eq.scala +++ /dev/null @@ -1,50 +0,0 @@ -package magnolia.examples - -import magnolia._ - -import language.experimental.macros -import collection.immutable.ListMap - -object `package` { - implicit class Equable[T: Eq](t: T) { - def isEqualTo(other: T): Boolean = implicitly[Eq[T]].isEqual(t, other) - } - - implicit val eqString: Eq[String] = _ == _ - implicit val eqBool: Eq[Boolean] = _ == _ - - implicit def eqList[T: Eq]: Eq[List[T]] = - (l1, l2) => l1.size == l2.size && (l1 zip l2).forall { case (e1, e2) => e1 isEqualTo e2 } - - implicit def eqSet[T: Eq]: Eq[Set[T]] = - (s1, s2) => s1.size == s2.size && (s1 zip s2).forall { case (e1, e2) => e1 isEqualTo e2 } -} - -sealed trait Tree -case class Branch(left: Tree, right: Tree) extends Tree -case class Leaf(value: Int) extends Tree - -sealed trait Entity -case class Person(name: String, address: Address) extends Entity -case class Organization(name: String, contacts: Set[Person]) extends Entity -case class Address(lines: List[String], country: Country) -case class Country(name: String, code: String, salesTax: Boolean) - -trait Eq[T] { def isEqual(a: T, b: T): Boolean } - -object Eq { - - implicit val eqInt: Eq[Int] = _ == _ - - val derivation: Coderivation2[Eq] = new Coderivation2[Eq] { - type Return = Boolean - - def call[T](eq: Eq[T], value1: T, value2: T): Boolean = - if(value1.getClass == value2.getClass) eq.isEqual(value1, value2) else false - - def construct[T](body: (T, T) => Boolean): Eq[T] = body(_, _) - def join(className: String, elements: ListMap[String, Boolean]): Boolean = - elements.forall(_._2) - } - -} diff --git a/examples/src/main/scala/typeclasses.scala b/examples/src/main/scala/typeclasses.scala new file mode 100644 index 0000000..bc5df94 --- /dev/null +++ b/examples/src/main/scala/typeclasses.scala @@ -0,0 +1,93 @@ +package magnolia.examples + +import scala.collection.immutable.ListMap +import scala.language.existentials +import scala.language.higherKinds + +import magnolia._ +import scala.reflect._ +import scala.reflect.macros._ +import scala.language.experimental.macros +import scala.annotation.unchecked.uncheckedVariance + + +object Show { + def join[T](construct: Any, className: String, elems: List[Param[Show, T]])(value: T): String = + elems.map { call => s"${call.label}=${call.typeclass.show(call.dereference(value))}" }.mkString(s"{", ",", "}") + + def split[T](subclasses: List[Magnolia.Subclass[Show, T]])(value: T): String = + subclasses.map { sub => sub.cast.andThen { value => + sub.typeclass.show(sub.cast(value)) + } }.reduce(_ orElse _)(value) + + implicit val string: Show[String] = identity + implicit val int: Show[Int] = new Show[Int] { def show(s: Int): String = s.toString } + implicit def generic[T]: Show[T] = macro Magnolia.generic[T] +} + +trait Show[T] { def show(value: T): String } + +object Eq { + def join[T](construct: Any, className: String, elems: List[Param[Eq, T]])(param1: T, param2: T): Boolean = + elems.forall { case call => call.typeclass.equal(call.dereference(param1), call.dereference(param2)) } + + def split[T](subclasses: List[Magnolia.Subclass[Eq, T]])(param1: T, param2: T): Boolean = + subclasses.map { case subclass => + subclass.cast.andThen { value => subclass.typeclass.equal(subclass.cast(param1), subclass.cast(param2)) } + }.reduce(_ orElse _)(param1) + + implicit val string: Eq[String] = _ == _ + implicit val int: Eq[Int] = _ == _ + implicit def generic[T]: Eq[T] = macro Magnolia.generic[T] +} + +trait Eq[T] { def equal(value: T, value2: T): Boolean } + +object Default { + case class Call[T](label: String, typeclass: Default[T]) + case class Subclass[T](label: String, typeclass: Default[T], cast: PartialFunction[_ >: T, T]) + + def join[T](construct: ((Call[R] => R) forSome { type R }) => T, className: String, elems: List[Call[_]]): T = + construct { call: Call[_] => call.typeclass.default } + + def split[T](subclasses: List[Subclass[T]])(param: T): T = subclasses.head.typeclass.default + + + implicit val string: Default[String] = new Default[String] { def default: String = "" } + implicit val int: Default[Int] = new Default[Int] { def default: Int = 0 } + implicit def generic[T]: Default[T] = macro Magnolia.generic[T] +} + +trait Default[T] { def default: T } + +object Decoder { + case class Call[T](label: String, typeclass: Decoder[T], value: String) + + case class Subclass[T](label: String, typeclass: Decoder[T], cast: PartialFunction[_ >: T, T]) + + def join[T](construct: ((Call[R] => R) forSome { type R }) => T, className: String, elems: List[Call[_]]): T = + construct { call: Call[_] => call.typeclass.decode(call.value) } + + def split[T](subclasses: List[Subclass[T]])(param: String): T = + subclasses.map { case Subclass(name, typeclass, cast) => + PartialFunction[String, T] { case _ if decodes(typeclass, param) => typeclass.decode(param) } + }.reduce(_ orElse _)(param) + + def decodes[T](tc: Decoder[T], s: String): Boolean = try { decodes(tc, s); true } catch { case e: Exception => false } + + implicit val string: Decoder[String] = new Decoder[String] { def decode(str: String): String = str } + implicit val int: Decoder[Int] = new Decoder[Int] { def decode(str: String): Int = str.toInt } + implicit def generic[T]: Decoder[T] = macro Magnolia.generic[T] +} + +trait Decoder[T] { def decode(str: String): T } + +sealed trait Tree +case class Leaf(value: String) extends Tree +case class Branch(left: Tree, right: Tree) extends Tree + +sealed trait Entity + +case class Company(name: String) extends Entity +case class Person(name: String, age: Int) extends Entity +case class Address(line1: String, occupant: Person) -- cgit v1.2.3