Skip to content

Instantly share code, notes, and snippets.

@GrafBlutwurst
Created June 4, 2021 14:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GrafBlutwurst/9eec62ca39a28abb0622ac1c87a2917e to your computer and use it in GitHub Desktop.
Save GrafBlutwurst/9eec62ca39a28abb0622ac1c87a2917e to your computer and use it in GitHub Desktop.
// 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