Created
June 4, 2021 14:11
-
-
Save GrafBlutwurst/9eec62ca39a28abb0622ac1c87a2917e to your computer and use it in GitHub Desktop.
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
// YOINKED from scala docs https://dotty.epfl.ch/docs/reference/contextual/derivation.html | |
// this is just so we have something derivable here to see it doesn't break with the stuff below | |
import scala.deriving.* | |
import scala.compiletime.{erasedValue, summonInline} | |
inline def summonAll[T <: Tuple]: List[Eq[_]] = | |
inline erasedValue[T] match | |
case _: EmptyTuple => Nil | |
case _: (t *: ts) => summonInline[Eq[t]] :: summonAll[ts] | |
trait Eq[T]: | |
def eqv(x: T, y: T): Boolean | |
object Eq: | |
given Eq[Int] with | |
def eqv(x: Int, y: Int) = x == y | |
def check(elem: Eq[_])(x: Any, y: Any): Boolean = | |
elem.asInstanceOf[Eq[Any]].eqv(x, y) | |
def iterator[T](p: T) = p.asInstanceOf[Product].productIterator | |
def eqSum[T](s: Mirror.SumOf[T], elems: => List[Eq[_]]): Eq[T] = | |
new Eq[T]: | |
def eqv(x: T, y: T): Boolean = | |
val ordx = s.ordinal(x) | |
(s.ordinal(y) == ordx) && check(elems(ordx))(x, y) | |
def eqProduct[T](p: Mirror.ProductOf[T], elems: => List[Eq[_]]): Eq[T] = | |
new Eq[T]: | |
def eqv(x: T, y: T): Boolean = | |
iterator(x).zip(iterator(y)).zip(elems.iterator).forall { | |
case ((x, y), elem) => check(elem)(x, y) | |
} | |
inline given derived[T](using m: Mirror.Of[T]): Eq[T] = | |
lazy val elemInstances = summonAll[m.MirroredElemTypes] | |
inline m match | |
case s: Mirror.SumOf[T] => eqSum(s, elemInstances) | |
case p: Mirror.ProductOf[T] => eqProduct(p, elemInstances) | |
end Eq | |
//END YOINK | |
//sadly it needs those traits for the encoding to work ;_; | |
sealed trait SharedDataDefinition: | |
def a: String | |
sealed trait PrivateDataDefinition: | |
def b: Int | |
type Data[F[_]] = SharedDataDefinition & F[PrivateDataDefinition] | |
type Public[A] = Any | |
type Private[A] = A | |
//just a show of prove of the subtyping relationship of a couple of noteworthy things | |
summon[SharedDataDefinition =:= Data[Public]] | |
summon[Data[Private] <:< Data[Public]] | |
final case class SharedData private (a:String) extends SharedDataDefinition | |
object SharedData: | |
def apply(a:String):Data[Public] = new SharedData(a) | |
def forget(data: Data[Public]):SharedData = data match | |
case shared: SharedData => shared | |
case PrivateData(shared, _) => shared | |
end SharedData | |
final case class PrivateData private (shared: SharedData, b:Int) extends SharedDataDefinition, PrivateDataDefinition: | |
//This new feature is neat, this means if we change the shared part we automaticaly also have them on this one | |
export shared.* | |
object PrivateData: | |
def apply(shared:SharedDataDefinition, b:Int):Data[Private] = shared match | |
case shared:SharedData => new PrivateData(shared,b) | |
end PrivateData | |
given Eq[Data[Public]] with | |
def eqv(x: Data[Public], y: Data[Public]) = SharedData.forget(x).a == SharedData.forget(y).a | |
//create the public part | |
val x: Data[Public] = SharedData("a") | |
// add the private part | |
val y: Data[Private] = PrivateData(x, 3) | |
//"forget" private part by upcasting (though in terms of inspectable runtime it's still a Private Data | |
val z: Data[Public] = y | |
//or "forget" by actually extracting the shared part | |
val z2: Data[Public] = SharedData.forget(y) | |
val eq = summon[Eq[Data[Public]]] | |
eq.eqv(x,z) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment