Skip to content

Instantly share code, notes, and snippets.

@johnynek
Created June 25, 2021 19:05
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save johnynek/1e3cbddf461bd3da9b00e2f4f126c253 to your computer and use it in GitHub Desktop.
Save johnynek/1e3cbddf461bd3da9b00e2f4f126c253 to your computer and use it in GitHub Desktop.
An example of zero cost addition of types on top of `Map[String, Any]` to model generic records in scala 3
package example
object RecMap {
object Record {
// use this scope to bound who can see inside the opaque type
opaque type Rec[A <: Tuple] = Map[String, Any]
object Rec {
type HasKey[A <: Tuple, K] =
A match
case (K, t) *: _ => t
case _ *: t => HasKey[t, K]
type UpdateKey[A <: Tuple, K, V] <: Tuple =
A match
case EmptyTuple => (K, V) *: EmptyTuple
case (K, _) *: t => (K, V) *: t
case head *: tail => head *: UpdateKey[tail, K, V]
val empty: Rec[EmptyTuple] = Map.empty
extension [A <: Tuple](toMap: Rec[A])
def apply[K <: String & Singleton](key: K): HasKey[A, K] =
toMap(key).asInstanceOf[HasKey[A, K]]
def +[K <: String & Singleton, V](kv: (K, V)): Rec[UpdateKey[A, K, V]] =
toMap + kv
}
}
def main(args: Array[String]) =
import Record._
val rec =
Rec.empty + ("str" -> "this is a string") + ("int" -> 42)
// rec knows the types of the different columns here
val strRes: String = rec("str")
val intRes: Int = rec("int")
println(strRes + intRes)
end main
}
@johnynek
Copy link
Author

Maybe not everything is totally safe yet:

scala/scala3#12974

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment