Skip to content

Commit d16dc96

Browse files
committed
Add typelevel label info to derivation strawman
1 parent ec2995f commit d16dc96

File tree

2 files changed

+84
-3
lines changed

2 files changed

+84
-3
lines changed

tests/run/typeclass-derivation2c.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,7 @@ ListBuffer(0, 0, 11, 0, 22, 0, 33, 1, 0, 0, 11, 0, 22, 1, 1)
44
Cons(Cons(11,Cons(22,Cons(33,Nil))),Cons(Cons(11,Cons(22,Nil)),Nil))
55
ListBuffer(1, 2)
66
Pair(1,2)
7+
Cons(hd = 11, tl = Cons(hd = 22, tl = Cons(hd = 33, tl = Nil())))
8+
Cons(hd = Cons(hd = 11, tl = Cons(hd = 22, tl = Cons(hd = 33, tl = Nil()))), tl = Cons(hd = Cons(hd = 11, tl = Cons(hd = 22, tl = Nil())), tl = Nil()))
9+
Cons(hd = Left(x = 1), tl = Cons(hd = Right(x = Pair(x = 2, y = 3)), tl = Nil()))
10+
Cons(hd = Left(x = 1), tl = Cons(hd = Right(x = Pair(x = 2, y = 3)), tl = Nil()))

tests/run/typeclass-derivation2c.scala

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ object TypeLevel {
2929

3030
abstract class GenericProduct[T] extends Generic[T] {
3131
type ElemTypes <: Tuple
32+
type CaseLabel
33+
type ElemLabels <: Tuple
3234
def toProduct(x: T): Product
3335
def fromProduct(p: Product): T
3436
}
@@ -63,6 +65,8 @@ object Lst {
6365

6466
class GenericCons[T] extends GenericProduct[Cons[T]] {
6567
type ElemTypes = (T, Lst[T])
68+
type CaseLabel = "Cons"
69+
type ElemLabels = ("hd", "tl")
6670
def toProduct(x: Cons[T]): Product = x
6771
def fromProduct(p: Product): Cons[T] =
6872
new Cons(p.productElement(0).asInstanceOf[T],
@@ -73,6 +77,8 @@ object Lst {
7377
case object Nil extends Lst[Nothing] {
7478
class GenericNil extends GenericProduct[Nil.type] {
7579
type ElemTypes = Unit
80+
type CaseLabel = "Nil"
81+
type ElemLabels = Unit
7682
def toProduct(x: Nil.type): Product = EmptyProduct
7783
def fromProduct(p: Product): Nil.type = Nil
7884
}
@@ -82,7 +88,7 @@ object Lst {
8288
// three clauses that could be generated from a `derives` clause
8389
implicit def derived$Eq[T: Eq]: Eq[Lst[T]] = Eq.derived
8490
implicit def derived$Pickler[T: Pickler]: Pickler[Lst[T]] = Pickler.derived
85-
//implicit def derived$Show[T: Show]: Show[Lst[T]] = Show.derived
91+
implicit def derived$Show[T: Show]: Show[Lst[T]] = Show.derived
8692
}
8793

8894
// A typeclass
@@ -148,6 +154,8 @@ object Pair {
148154

149155
class GenericPair[T] extends GenericProduct[Pair[T]] {
150156
type ElemTypes = (T, T)
157+
type CaseLabel = "Pair"
158+
type ElemLabels = ("x", "y")
151159
def toProduct(x: Pair[T]): Product = x
152160
def fromProduct(p: Product): Pair[T] =
153161
Pair(p.productElement(0).asInstanceOf, p.productElement(1).asInstanceOf)
@@ -157,7 +165,7 @@ object Pair {
157165
// clauses that could be generated from a `derives` clause
158166
implicit def derived$Eq[T: Eq]: Eq[Pair[T]] = Eq.derived
159167
implicit def derived$Pickler[T: Pickler]: Pickler[Pair[T]] = Pickler.derived
160-
//implicit def derived$Show[T: Show]: Show[Pair[T]] = Show.derived
168+
implicit def derived$Show[T: Show]: Show[Pair[T]] = Show.derived
161169
}
162170

163171
sealed trait Either[+L, +R] extends Product with Serializable // derives Eq, Pickler, Show
@@ -181,7 +189,7 @@ object Either {
181189

182190
implicit def derived$Eq[L: Eq, R: Eq]: Eq[Either[L, R]] = Eq.derived
183191
implicit def derived$Pickler[L: Pickler, R: Pickler]: Pickler[Either[L, R]] = Pickler.derived
184-
//implicit def derived$Show[L: Show, R: Show]: Show[Either[L, R]] = Show.derived
192+
implicit def derived$Show[L: Show, R: Show]: Show[Either[L, R]] = Show.derived
185193
}
186194

187195
case class Left[L](elem: L) extends Either[L, Nothing]
@@ -191,6 +199,8 @@ object Left {
191199
import TypeLevel._
192200
class GenericLeft[L] extends GenericProduct[Left[L]] {
193201
type ElemTypes = L *: Unit
202+
type CaseLabel = "Left"
203+
type ElemLabels = "x" *: Unit
194204
def toProduct(x: Left[L]) = x
195205
def fromProduct(p: Product): Left[L] = Left(p.productElement(0).asInstanceOf[L])
196206
}
@@ -201,6 +211,8 @@ object Right {
201211
import TypeLevel._
202212
class GenericRight[R] extends GenericProduct[Right[R]] {
203213
type ElemTypes = R *: Unit
214+
type CaseLabel = "Right"
215+
type ElemLabels = "x" *: Unit
204216
def toProduct(x: Right[R]) = x
205217
def fromProduct(p: Product): Right[R] = Right(p.productElement(0).asInstanceOf[R])
206218
}
@@ -298,6 +310,63 @@ object Pickler {
298310
}
299311
}
300312

313+
// A third typeclass, making use of labels
314+
trait Show[T] {
315+
def show(x: T): String
316+
}
317+
object Show {
318+
import scala.compiletime.{erasedValue, constValue}
319+
import TypeLevel._
320+
321+
inline def tryShow[T](x: T): String = implicit match {
322+
case s: Show[T] => s.show(x)
323+
}
324+
325+
inline def showElems[Elems <: Tuple, Labels <: Tuple](x: Product, n: Int): List[String] =
326+
inline erasedValue[Elems] match {
327+
case _: (elem *: elems1) =>
328+
inline erasedValue[Labels] match {
329+
case _: (label *: labels1) =>
330+
val formal = constValue[label]
331+
val actual = tryShow(x.productElement(n).asInstanceOf[elem])
332+
s"$formal = $actual" :: showElems[elems1, labels1](x, n + 1)
333+
}
334+
case _: Unit =>
335+
Nil
336+
}
337+
338+
inline def showCase[T](gp: GenericProduct[T], x: T): String = {
339+
val labl = constValue[gp.CaseLabel]
340+
showElems[gp.ElemTypes, gp.ElemLabels](gp.toProduct(x), 0)
341+
.mkString(s"$labl(", ", ", ")")
342+
}
343+
344+
inline def showCases[T](x: T, genSum: GenericSum[T], ord: Int, n: Int): String =
345+
inline if (n == genSum.numberOfCases)
346+
""
347+
else if (ord == n)
348+
inline genSum.alternative(n) match {
349+
case cas: GenericProduct[p] =>
350+
showCase(cas, x.asInstanceOf[p])
351+
}
352+
else showCases[T](x, genSum, ord, n + 1)
353+
354+
inline def derived[T](implicit ev: Generic[T]): Show[T] = new {
355+
def show(x: T): String = {
356+
inline ev match {
357+
case ev: GenericSum[T] =>
358+
showCases(x, ev, ev.ordinal(x), 0)
359+
case ev: GenericProduct[p] =>
360+
showCase(ev, x)
361+
}
362+
}
363+
}
364+
365+
implicit object IntShow extends Show[Int] {
366+
def show(x: Int): String = x.toString
367+
}
368+
}
369+
301370
// Tests
302371
object Test extends App {
303372
import TypeLevel._
@@ -349,7 +418,14 @@ object Test extends App {
349418
assert(p1 == p1a)
350419
assert(eqp.eql(p1, p1a))
351420

421+
def showPrintln[T: Show](x: T): Unit =
422+
println(implicitly[Show[T]].show(x))
423+
424+
showPrintln(xs)
425+
showPrintln(xss)
426+
352427
val zs = Lst.Cons(Left(1), Lst.Cons(Right(Pair(2, 3)), Lst.Nil))
428+
showPrintln(zs)
353429

354430
def pickle[T: Pickler](buf: mutable.ListBuffer[Int], x: T): Unit =
355431
implicitly[Pickler[T]].pickle(buf, x)
@@ -366,5 +442,6 @@ object Test extends App {
366442
def eql[T: Eq](x: T, y: T) = implicitly[Eq[T]].eql(x, y)
367443

368444
val zs1 = copy(zs)
445+
showPrintln(zs1)
369446
assert(eql(zs, zs1))
370447
}

0 commit comments

Comments
 (0)