Last active
March 8, 2020 07:51
-
-
Save juanjovazquez/f498ccbfe29936e3ccce0b804f502b4a to your computer and use it in GitHub Desktop.
Records à la shapeless in dotty
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 labelled { | |
opaque type Field[K <: String, +V] = V | |
object Field { | |
def apply[K <: String, V](v: V): Field[K, V] = v | |
extension on [K <: String, V](field: Field[K, V]) { | |
def value: V = field | |
} | |
} | |
class FieldBuilder[K <: String] { | |
def apply[V](v: V): Field[K, V] = Field(v) | |
} | |
def field[K <: String] = FieldBuilder[K] | |
} | |
object record { | |
import labelled._ | |
import scala.compiletime._ | |
trait Selector[T <: Tuple, K] { | |
type Out | |
def apply(t: T): Out | |
} | |
object Selector { | |
type Aux[T <: Tuple, K <: String, Out0] = Selector[T, K] { type Out = Out0 } | |
inline def apply[T <: Tuple, K <: String] | |
(using selector: Selector[T, K]): Aux[T, K, selector.Out] = selector | |
given Found[K <: String, V, T <: Tuple] as Selector[Field[K, V] *: T, K] { | |
type Out = V | |
def apply(t: Field[K, V] *: T): V = t.head.value | |
} | |
// `Selector.Aux` did the trick for full type inference. | |
// See https://bit.ly/2TvuRYj for discussion | |
given KeepFinding[K <: String, K1 <: String, V, V1, T <: Tuple] | |
(using ts: Selector.Aux[T, K, V]) as Selector[Field[K1, V1] *: T, K] { | |
type Out = ts.Out | |
def apply(t: Field[K1, V1] *: T): Out = ts(t.tail) | |
} | |
} | |
} | |
import labelled._, record._ | |
extension on [T <: Tuple, K <: String](t: T) { | |
def get(k: K)(using selector: Selector[T, k.type]): selector.Out = selector(t) | |
} | |
extension FieldOps on [K <: String, V](field: Field[K, V]) { | |
inline def label <: String = scala.compiletime.constValue[K] | |
} | |
extension on [K <: String, V](label: K) { | |
def ->> (v: V): Field[label.type, V] = field[label.type](v) | |
} | |
object RecordsApp extends App { | |
val isb = ("i" ->> 10) *: ("s" ->> "foo") *: ("b" ->> true) *: () | |
val label = isb.head.label | |
println(label) // i | |
val value = isb.head.value | |
println(value) // 10 | |
val i: Int = isb.get("i") | |
println(i) // 10 | |
val foo: String = isb.get("s") | |
println(foo) // foo | |
val b: Boolean = isb.get("b") | |
println(b) // true | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment