scalatypespattern-matchingscala-3path-dependent-type

Scala 3. Implementing Dependent Function Type


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


Solution

  • 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

    Scala 3: typed tuple zipping

    Express function of arbitrary arity in vanilla Scala 3

    Shapeless3 and annotations

    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