Skip to content

Instantly share code, notes, and snippets.

@juanjovazquez
Last active March 8, 2020 07:51
Show Gist options
  • Save juanjovazquez/f498ccbfe29936e3ccce0b804f502b4a to your computer and use it in GitHub Desktop.
Save juanjovazquez/f498ccbfe29936e3ccce0b804f502b4a to your computer and use it in GitHub Desktop.
Records à la shapeless in dotty
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