Created
March 10, 2021 14:40
-
-
Save bishabosha/5e081f3d74292fe07f3c12384e572de3 to your computer and use it in GitHub Desktop.
Demonstrate using Opaque Type Aliases as evidence parameters to abstract over different storage
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
object Notarisation: | |
private case object Proven | |
opaque type Proof <: Singleton = Proven.type | |
given Proof = Proven | |
end Notarisation | |
import Notarisation.Proof | |
object Entities: | |
opaque type Entity[+T] = Long | |
object Entity: | |
def apply[T <: Proof](id: Long)(using T): Entity[T] = id | |
extension [T](e: Entity[T]) def id: Long = e | |
end Entities | |
import Entities.Entity | |
object Db: | |
trait Storage: | |
def newId: Long | |
def firstName(id: Long): String | |
def setFirstName(id: Long, firstName: String): Unit | |
def age(id: Long): Int | |
def setAge(id: Long, age: Int): Unit | |
end Db | |
object MyData: | |
opaque type Kind <: Singleton = Proof | |
object Kind: | |
opaque type Person <: Kind = Proof | |
// The only place you can create an Entity[Person] | |
def newPerson(firstName: String, age: Int)(using Db.Storage): Entity[Kind.Person] = | |
val id = summon[Db.Storage].newId | |
summon[Db.Storage].setFirstName(id, firstName) | |
summon[Db.Storage].setAge(id, age) | |
Entity(id) | |
extension (person: Entity[Kind.Person]) | |
def firstName(using Db.Storage) = summon[Db.Storage].firstName(person.id) | |
def age(using Db.Storage) = summon[Db.Storage].age(person.id) | |
end MyData | |
given Db.Storage with | |
private var _id: Long = 0 | |
private val _firstName: Array[String] = new Array(25) | |
private val _age: Array[Int] = new Array(25) | |
def newId: Long = { val id: Long = _id; _id += 1; id } | |
def firstName(id: Long): String = _firstName(id.toInt) | |
def setFirstName(id: Long, firstName: String): Unit = _firstName(id.toInt) = firstName | |
def age(id: Long): Int = _age(id.toInt) | |
def setAge(id: Long, age: Int): Unit = _age(id.toInt) = age | |
val Jamie = MyData.Kind.newPerson("Jamie", 24) | |
val Georgie = MyData.Kind.newPerson("Georgie", 21) | |
// val ?? = Entity[MyData.Kind.Person](56) // <- impossible | |
Jamie.firstName | |
Jamie.age | |
Georgie.firstName | |
Georgie.age |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment