Created
June 25, 2021 19:05
-
-
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
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
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 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Maybe not everything is totally safe yet:
scala/scala3#12974