Created
April 22, 2020 22:27
-
-
Save harpocrates/950d58c5cb2af76b5c65227dbd22599d to your computer and use it in GitHub Desktop.
Utility for debugging typeclass derivation
This file contains 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 $ivy.`com.chuusai::shapeless:2.3.3` | |
import $ivy.`io.spray::spray-json:1.3.5` | |
import shapeless._ | |
import scala.reflect.runtime.universe.WeakTypeTag | |
import scala.collection.mutable | |
import spray.json._ | |
sealed trait DataTypeDerivation { | |
def name: String | |
def sort: String | |
def foundInstance: Boolean | |
def json(stackLimit: Int): JsValue = { | |
val seenDerivations = mutable.Set.empty[String] | |
def visitDerivation(dd: DataTypeDerivation, depth: Int): JsValue = { | |
if (depth > stackLimit) | |
return JsString(s"Stackoverflow: ${dd.name} at depth ${depth}") | |
if (!seenDerivations.add(dd.name)) { | |
JsString(s"Skipping: ${dd.name}") | |
} else { | |
val jsFields = Map( | |
"name" -> JsString(dd.name), | |
"instance_found" -> JsBoolean(dd.foundInstance), | |
"type" -> JsString(dd.sort) | |
) | |
dd match { | |
case p: DataTypeDerivation.ProductOf if p.fields.value.nonEmpty => | |
val fields = p.fields.value.map(visitDerivation(_, depth + 1)) | |
JsObject(jsFields + ("fields" -> JsArray(fields.toVector))) | |
case c: DataTypeDerivation.CoproductOf if c.variants.value.nonEmpty => | |
val variants = c.variants.value.map(visitDerivation(_, depth + 1)) | |
JsObject(jsFields + ("variants" -> JsArray(variants.toVector))) | |
case _ => | |
JsObject(jsFields) | |
} | |
} | |
} | |
visitDerivation(this, depth = 0) | |
} | |
} | |
object DataTypeDerivation { | |
case class ProductOf( | |
name: String, | |
fields: Lazy[Seq[DataTypeDerivation]], | |
foundInstance: Boolean | |
) extends DataTypeDerivation { | |
def sort = "product" | |
} | |
case class CoproductOf( | |
name: String, | |
variants: Lazy[Seq[DataTypeDerivation]], | |
foundInstance: Boolean | |
) extends DataTypeDerivation { | |
def sort = "coproduct" | |
} | |
case class Atomic( | |
name: String, | |
foundInstance: Boolean | |
) extends DataTypeDerivation { | |
def sort = "atomic" | |
} | |
} | |
class OptionalImplicit[A](val value: Option[A]) | |
object OptionalImplicit extends LowPrioityOptionalImplicit { | |
implicit def found[A](implicit a: A): OptionalImplicit[A] = new OptionalImplicit(Some(a)) | |
} | |
trait LowPrioityOptionalImplicit { | |
implicit def notFound[A]: OptionalImplicit[A] = new OptionalImplicit(None) | |
} | |
object DebugNestedDerivation { | |
def apply[F[_], A](implicit d: Derivation[F, A]): DataTypeDerivation = d.derivation | |
class Derivation[F[_], A](val derivation: DataTypeDerivation) | |
object Derivation extends LowPriorityDerivation { | |
implicit def product[F[_], A,G <: HList](implicit | |
gen: Generic.Aux[A,G], | |
ct: WeakTypeTag[A], | |
subs: SubDerivation[F, G], | |
found: OptionalImplicit[Lazy[F[A]]] | |
): Derivation[F, A] = new Derivation(DataTypeDerivation.ProductOf( | |
name = ct.tpe.toString(), | |
fields = Lazy(subs.derivations), | |
foundInstance = found.value.nonEmpty | |
)) | |
implicit def coproduct[F[_], A,G <: Coproduct](implicit | |
gen: Generic.Aux[A,G], | |
ct: WeakTypeTag[A], | |
subs: SubDerivation[F, G], | |
found: OptionalImplicit[Lazy[F[A]]] | |
): Derivation[F, A] = new Derivation(DataTypeDerivation.CoproductOf( | |
name = ct.tpe.toString(), | |
variants = Lazy(subs.derivations), | |
foundInstance = found.value.nonEmpty | |
)) | |
} | |
trait LowPriorityDerivation { | |
implicit def atomic[F[_], A](implicit | |
ct: WeakTypeTag[A], | |
found: OptionalImplicit[Lazy[F[A]]] | |
): Derivation[F, A] = new Derivation(DataTypeDerivation.Atomic( | |
name = ct.tpe.toString(), | |
foundInstance = found.value.nonEmpty | |
)) | |
} | |
trait SubDerivation[F[_], A] { | |
def derivations: Seq[DataTypeDerivation] | |
} | |
object SubDerivation { | |
implicit def hnilSub[F[_]] = new SubDerivation[F, HNil] { | |
def derivations = Seq.empty | |
} | |
implicit def cnilSub[F[_]] = new SubDerivation[F, CNil] { | |
def derivations = Seq.empty | |
} | |
implicit def hconsSub[F[_], A,B <: HList](implicit | |
derivation: Lazy[Derivation[F, A]], | |
rest: SubDerivation[F, B] | |
): SubDerivation[F, A :: B] = new SubDerivation[F, A :: B] { | |
def derivations = derivation.value.derivation +: rest.derivations | |
} | |
implicit def cconsSub[F[_], A,B <: Coproduct](implicit | |
derivation: Lazy[Derivation[F, A]], | |
rest: SubDerivation[F, B] | |
): SubDerivation[F, A :+: B] = new SubDerivation[F, A :+: B] { | |
def derivations = derivation.value.derivation +: rest.derivations | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment