aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Pretty <jon.pretty@propensive.com>2017-10-30 13:58:33 +0100
committerJon Pretty <jon.pretty@propensive.com>2017-10-30 13:58:33 +0100
commit86c66ba93b5dc15301ac305fa28e24952e232613 (patch)
tree74e943910b29dfcae31ef8f943eb96eebcb34813
parent8edaa6e2adbb4aa724a7829be5003fdabb01628a (diff)
downloadmagnolia-86c66ba93b5dc15301ac305fa28e24952e232613.tar.gz
magnolia-86c66ba93b5dc15301ac305fa28e24952e232613.tar.bz2
magnolia-86c66ba93b5dc15301ac305fa28e24952e232613.zip
All variants now deriving
Though there's a stack overflow when trying to derive a `Decoder`.
-rw-r--r--core/src/main/scala/magnolia.scala17
-rw-r--r--examples/src/main/scala/typeclasses.scala50
-rw-r--r--tests/src/main/scala/main.scala23
3 files changed, 53 insertions, 37 deletions
diff --git a/core/src/main/scala/magnolia.scala b/core/src/main/scala/magnolia.scala
index 9da9f1e..c4c8113 100644
--- a/core/src/main/scala/magnolia.scala
+++ b/core/src/main/scala/magnolia.scala
@@ -20,6 +20,12 @@ trait Param[Tc[_], T] {
def dereference(param: T): S
}
+trait JoinContext[Tc[_], T] {
+ def construct[R](param: ((Param[Tc, T]) => Any)): T
+ def typeName: String
+ def parameters: List[Param[Tc, T]]
+}
+
object Magnolia {
import CompileTimeState._
@@ -156,10 +162,17 @@ object Magnolia {
}"""
}
- val constructor = q"null" //q"""{ => new ${}(..$params) }"""
+ val constructor = q"""new $genericType(..${callables.zip(implicits).map { case (call, imp) =>
+ q"fn($call).asInstanceOf[${imp._1.returnType}]"
+ } })"""
val impl = q"""
- ${c.prefix}.join(${constructor}, $className, _root_.scala.List(..$callables))
+ ${c.prefix}.join(new _root_.magnolia.JoinContext[$typeConstructor, $genericType] {
+ def construct[R](fn: ((Param[${typeConstructor}, $genericType]) => Any)): $genericType = $constructor
+ def typeName: _root_.java.lang.String = $className
+ def parameters: _root_.scala.List[Param[$typeConstructor, $genericType]] =
+ _root_.scala.List(..$callables)
+ })
"""
q"""
diff --git a/examples/src/main/scala/typeclasses.scala b/examples/src/main/scala/typeclasses.scala
index bc5df94..d3017e5 100644
--- a/examples/src/main/scala/typeclasses.scala
+++ b/examples/src/main/scala/typeclasses.scala
@@ -12,10 +12,11 @@ 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 join[T](context: JoinContext[Show, T])(value: T): String = context.parameters.map { param =>
+ s"${param.label}=${param.typeclass.show(param.dereference(value))}"
+ }.mkString(s"{", ",", "}")
- def split[T](subclasses: List[Magnolia.Subclass[Show, T]])(value: T): String =
+ def split[T](subclasses: List[Subclass[Show, T]])(value: T): String =
subclasses.map { sub => sub.cast.andThen { value =>
sub.typeclass.show(sub.cast(value))
} }.reduce(_ orElse _)(value)
@@ -28,13 +29,13 @@ object Show {
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 join[T](context: JoinContext[Eq, T])(value1: T, value2: T): Boolean =
+ context.parameters.forall { param => param.typeclass.equal(param.dereference(value1), param.dereference(value2)) }
- def split[T](subclasses: List[Magnolia.Subclass[Eq, T]])(param1: T, param2: T): Boolean =
+ def split[T](subclasses: List[Subclass[Eq, T]])(value1: T, value2: T): Boolean =
subclasses.map { case subclass =>
- subclass.cast.andThen { value => subclass.typeclass.equal(subclass.cast(param1), subclass.cast(param2)) }
- }.reduce(_ orElse _)(param1)
+ subclass.cast.andThen { value => subclass.typeclass.equal(subclass.cast(value1), subclass.cast(value2)) }
+ }.reduce(_ orElse _)(value1)
implicit val string: Eq[String] = _ == _
implicit val int: Eq[Int] = _ == _
@@ -44,36 +45,31 @@ object Eq {
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
+ def join[T](context: JoinContext[Default, T]): Default[T] = new Default[T] {
+ def default = context.construct { param => param.typeclass.default }
+ }
+ def split[T](subclasses: List[Subclass[Default, T]])(): Default[T] = new Default[T] {
+ def default = 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 val string: Default[String] = new Default[String] { def default = "" }
+ implicit val int: Default[Int] = new Default[Int] { def default = 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 join[T](context: JoinContext[Decoder, T])(value: String): T =
+ context.construct { param => param.typeclass.decode(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) }
+ def split[T](subclasses: List[Subclass[Decoder, T]])(param: String): T =
+ subclasses.map { subclass =>
+ { case _ if decodes(subclass.typeclass, param) => subclass.typeclass.decode(param) }: PartialFunction[String, T]
}.reduce(_ orElse _)(param)
- def decodes[T](tc: Decoder[T], s: String): Boolean = try { decodes(tc, s); true } catch { case e: Exception => false }
+ def decodes[T](tc: Decoder[T], s: String): Boolean = try { tc.decode(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 }
diff --git a/tests/src/main/scala/main.scala b/tests/src/main/scala/main.scala
index 7dee065..99ffe56 100644
--- a/tests/src/main/scala/main.scala
+++ b/tests/src/main/scala/main.scala
@@ -11,6 +11,7 @@ import scala.util._
object Tests extends TestApp {
def tests() = {
+ import examples._
test("construct a Show product instance") {
import examples._
@@ -42,40 +43,46 @@ object Tests extends TestApp {
Eq.generic[Tree].equal(Branch(Leaf("one"), Leaf("two")), Branch(Leaf("one"), Leaf("two")))
}.assert(_ == true)
- /*test("construction of Show instance for Leaf") {
+ test("construct a default value") {
+ Default.generic[Entity].default
+ }.assert(_ == (Company(""): Entity))
+
+ test("construction of Show instance for Leaf") {
scalac"""
import magnolia.examples._
implicitly[Show[Leaf]]
"""
- }.assert(_ == Returns(fqt"magnolia.examples.Show[magnolia.examples.Leaf]"))
+ }.assert(_ == (Returns(fqt"magnolia.examples.Show[magnolia.examples.Leaf]"): Compilation))
test("construction of Show instance for Tree") {
scalac"""
import magnolia.examples._
implicitly[Show[Tree]]
"""
- }.assert(_ == Returns(fqt"magnolia.examples.Show[magnolia.examples.Tree]"))
+ }.assert(_ == (Returns(fqt"magnolia.examples.Show[magnolia.examples.Tree]"): Compilation))
test("serialize a Leaf") {
- import magnolia.examples._
implicitly[Show[Leaf]].show(Leaf("testing"))
}.assert(_ == "{value=testing}")
test("serialize a Branch as a Tree") {
- import magnolia.examples._
implicitly[Show[Tree]].show(Branch(Leaf("LHS"), Leaf("RHS")))
}.assert(_ == "{left={value=LHS},right={value=RHS}}")
+ /*test("construct a decoder") {
+ Decoder.generic[Tree].decode("string")
+ }.assert(_ == (Leaf("something"): Tree))*/
+
test("show error stack") {
scalac"""
import magnolia.examples._
- case class Alpha(integer: Int)
+ case class Alpha(integer: Double)
case class Beta(alpha: Alpha)
Show.generic[Beta]
"""
- }.assert(_ == TypecheckError(txt"""magnolia: could not find typeclass for type Int
+ }.assert(_ == (TypecheckError(txt"""magnolia: could not find typeclass for type Double
| in parameter 'integer' of product type Alpha
| in parameter 'alpha' of product type Beta
- |"""))*/
+ |"""): Compilation))
}
}