Skip to content

Instantly share code, notes, and snippets.

@ChristopherDavenport
Last active December 10, 2020 22:13
Show Gist options
  • Save ChristopherDavenport/02217ed65898175577ae3feed19ee86a to your computer and use it in GitHub Desktop.
Save ChristopherDavenport/02217ed65898175577ae3feed19ee86a to your computer and use it in GitHub Desktop.
Generic Show Derivation of Case Classes using Scala 3
package io.chrisdavenport.shapelessless
import cats._
import cats.syntax.all._
import cats.effect._
import scala.deriving._
import scala.compiletime._
import org.tpolecat.typename._
// Primarily from learning from
// https://github.com/scodec/scodec/blob/5e46cc8d85f8ad99bf65983f86f24076568a492c/shared/src/main/scala/scodec/Codec.scala#L633-L634
object Main extends IOApp:
case class Foo(i: Int, b: String)
import io.chrisdavenport.shapelessless.CaseClassFun.{_, given}
def run(args: List[String]): IO[ExitCode] = {
val foo = Foo(1, "bar")
val x = summon[Show[Foo]]
// IO(println("foo"))
IO(println(show"$foo")) // Foo(i=1,b=bar)
.as(ExitCode.Success)
}
object CaseClassFun:
private inline def encodeElems[A <: Tuple, L <: Tuple](a: Any, i: Int): String =
inline erasedValue[A] match {
case _: (hd *: EmptyTuple) =>
inline erasedValue[L] match {
case _ :(hdLabel *: tlLabels) =>
val hdLabelValue = constValue[hdLabel].asInstanceOf[String]
val show = summonInline[Show[hd]]
hdLabelValue ++ "=" ++
show.show(a.asInstanceOf[Product].productElement(i).asInstanceOf[hd])
case EmptyTuple => sys.error("not possible - label for product not available")
}
case _: (hd *: tl) =>
inline erasedValue[L] match {
case _ :(hdLabel *: tlLabels) =>
val hdLabelValue = constValue[hdLabel].asInstanceOf[String]
val show = summonInline[Show[hd]]
hdLabelValue ++ "=" ++
show.show(a.asInstanceOf[Product].productElement(i).asInstanceOf[hd]) ++ "," ++
encodeElems[tl, tlLabels](a, i + 1)
case EmptyTuple => sys.error("not possible - label for product not available")
}
case EmptyTuple => ""
}
inline given [P <: Product](
using
t: TypeName[P],
m: Mirror.ProductOf[P],
) as Show[P] =
{(p: P) =>
simpleTypeName(t.value) ++ "(" ++
encodeElems[m.MirroredElemTypes, m.MirroredElemLabels](p, 0)
++ ")"
}
// scala.collection.immutable.List[scala.Option[scala.Int]] => List
// io.chrisdavenport.shapelessless.Main.Foo => Foo
def simpleTypeName(string: String): String = {
val stop = string.indexOf("[")
val topClass =
if (stop >= 0) string.substring(0, stop)
else string
val indexOfPeriodInTop = topClass.lastIndexOf(".")
val simpleTopType = if (indexOfPeriodInTop >= 0) topClass.substring(indexOfPeriodInTop + 1, topClass.length)
else topClass
simpleTopType
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment