Created
March 29, 2019 11:35
-
-
Save fsarradin/178876f079f29aa1092a9326899043ef to your computer and use it in GitHub Desktop.
Typeclass derivation in Dotty
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import scala.reflect._ | |
import scala.compiletime._ | |
import scala.compiletime.Shape._ | |
enum Maybe[+A] derives ToJson { | |
case Just(value: A) | |
case Nope | |
} | |
case class Person(id: String, name: String, age: Int) derives ToJson | |
object DeriveMain { | |
import ToJson._ | |
// alternative declarations: | |
// implied [A: ToJson] for ToJson[Maybe[A]] = ToJson.derived | |
// implied for ToJson[Person] = ToJson.derived | |
def main(args: Array[String]): Unit = { | |
val a: Maybe[Person] = Maybe.Just(Person("1", "john", 32)) | |
println(a.toJson) | |
} | |
} | |
trait ToJson[A] { | |
def (value: A) toJson: String | |
} | |
object ToJson { | |
inline def derived[A] given (ev: Generic[A]): ToJson[A] = | |
new ToJson[A] { | |
def (value: A) toJson: String = { | |
val mvalue = ev.reflect(value) | |
inline erasedValue[ev.Shape] match { | |
case t: Cases[alts] => | |
toJsonCases[alts](mvalue) | |
case _: Case[_, elems] => | |
"{ " + toJsonElems[elems](mvalue) + " }" | |
} | |
} | |
} | |
inline def toJsonCases[Alts <: Tuple](mvalue: Mirror, n: Int = 0): String = | |
inline erasedValue[Alts] match { | |
case _: (Case[_, elems] *: tail) => | |
if (mvalue.ordinal == n) | |
"{ " + toJsonElems[elems](mvalue) + " }" | |
else | |
toJsonCases[tail](mvalue, n + 1) | |
case _: Unit => | |
throw new MatchError(mvalue.ordinal) | |
} | |
inline def toJsonElems[Elems <: Tuple](mvalue: Mirror, n: Int = 0): String = | |
inline erasedValue[Elems] match { | |
case _: (elem *: Unit) => | |
toJsonElem[elem]( | |
mvalue.adtClass.label(mvalue.ordinal)(n + 1), | |
mvalue(n).asInstanceOf[elem]) | |
case _: (elem *: tail) => | |
(toJsonElem[elem]( | |
mvalue.adtClass.label(mvalue.ordinal)(n + 1), | |
mvalue(n).asInstanceOf[elem]) | |
+ ", " + toJsonElems[tail](mvalue, n + 1)) | |
case _: Unit => "" | |
} | |
inline def toJsonElem[A](label: String, value: A): String = | |
implicit match { | |
case ev: ToJson[A] => | |
s""""$label": ${ev.toJson(value)}""" | |
case _ => | |
error(s"No ToJson instance for $value") | |
} | |
implied for ToJson[Int] { | |
def (value: Int) toJson: String = s"$value" | |
} | |
implied for ToJson[String] { | |
def (value: String) toJson: String = s""""$value"""" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment