Learning Scala 3.
how can i make this code type-match and compile?
trait Key {
type Value
}
object Name extends Key {
type Value = String
}
object Age extends Key {
type Value = Int
}
type DB = (k: Key) => Option[k.Value]
val dbImpl: DB = (k: Key) => {
k match {
case Name => Some("abc") // this does not compile, how can i make it sniff Value type is String automatically?
case Age => None
}
}
thanks
As a workaround you can try match types
trait Key
object Name extends Key
object Age extends Key
type OptValue[K <: Key] = K match
case Name.type => Option[String]
case Age.type => Option[Int]
def dbImpl[K <: Key](k: K): OptValue[K] = k match
case _: Name.type => Some("abc")
case _: Age.type => None
or
val dbImpl: [K <: Key] => K => OptValue[K] =
[K <: Key] => (k: K) => k match
case _: Name.type => Some("abc")
case _: Age.type => None
I'm reminding that
type DB = [K <: Key] => K => OptValue[K]
val dbImpl: DB = [K <: Key] => (k: K) => k match
case _: Name.type => Some("abc")
case _: Age.type => None
or
def dbImpl[K <: Key](k: K): OptValue[K] = k match
case Name => Some("abc")
case Age => None
or
type Value[K <: Key] = K match
case Name.type => String
case Age.type => Int
def dbImpl[K <: Key](k: K): Option[Value[K]] = k match
case _: Name.type => Some("abc")
case _: Age.type => None
will not work.
scala 3 map tuple to futures of tuple types and back
Express function of arbitrary arity in vanilla Scala 3
How to get match type to work correctly in Scala 3
Another option is type classes
trait Key
object Name extends Key
object Age extends Key
// type class
trait KeyValue[K <: Key]:
type Value
type Out = Option[Value]
def apply(k: K): Out
object KeyValue:
type Aux[K <: Key, V] = KeyValue[K] { type Value = V }
def instance[K <: Key, V](f: K => Option[V]): Aux[K, V] = new KeyValue[K]:
override type Value = V
override def apply(k: K): Out = f(k)
given Aux[Name.type, String] = instance(_ => Some("abc"))
given Aux[Age.type, Int] = instance(_ => None)
def dbImpl[K <: Key](k: K)(using kv: KeyValue[K]): kv.Out = kv(k)
One more option is inlining and using scala.compiletime.summonFrom
trait Key:
type Value
object Name extends Key:
override type Value = String
object Age extends Key:
override type Value = Int
inline def dbImpl(k: Key): Option[k.Value] = inline k match
case Name => summonFrom {
case _: (String =:= k.Value) => Some("abc")
}
case Age => summonFrom {
case _: (Option[Int] =:= Option[k.Value]) => None: Option[Int]
}
The easiest is to make Value
a type parameter rather than type member
trait Key[Value]
object Name extends Key[String]
object Age extends Key[Int]
def dbImpl[V](k: Key[V]): Option[V] = k match
case Name => Some("abc")
case Age => None
This implementation compiles while
trait Key:
type Value
object Name extends Key:
override type Value = String
object Age extends Key:
override type Value = Int
def dbImpl[V](k: Key {type Value = V}): Option[V] = k match
case Name => Some("abc")
case Age => None
or
trait Key:
type Value
object Key:
type Aux[V] = Key { type Value = V }
object Name extends Key:
override type Value = String
object Age extends Key:
override type Value = Int
def dbImpl[V](k: Key.Aux[V]): Option[V] = k match
case Name => Some("abc")
case Age => None
or
trait Key:
type Value
object Name extends Key:
override type Value = String
object Age extends Key:
override type Value = Int
def dbImpl(k: Key): Option[k.Value] = k match
case Name => Some("abc")
case Age => None
doesn't. (Scala 3.2.2)
Aleksander Boruch-Gruszecki. GADTs in Dotty https://www.youtube.com/watch?v=VV9lPg3fNl8
Dale Wijnand. Space Engine for Pattern Matching https://www.youtube.com/watch?v=yaxJPIsy4Js