Extend with dependent type

184 views Asked by At

Here is the minimal reproduction of my problem:

  trait Superclass[T]
  
  class A(val key: Any) extends Superclass[key.type]
  
  val x: Superclass["123"] = A("123")

As you can see, I'm trying to encode the statically known type of Key into the type parameter. It's not like I can put the type of key as a type parameter of A, since in my real example, it's more like class A(using g: blablah)(val key: g.Key) extends SuperClass[g.Mapping[key.type]] but that's not relevant for the problem above.

Is it just that we can't use dependent types inside the arguments of the super class?

Update

The encoding with type members yields the same error:

  trait Superclass {
    type T
  }
  
  class A(val key: Any) extends Superclass {
    type T = key.type
  }
  
  val x: Superclass & {type T = "123"} = A("123")

I still get

[error] 14 |  val x: Superclass & {type T = "123"} = A("123")
[error]    |                                         ^^^^^^^^
[error]    |                Found:    Main.A
[error]    |                Required: Main.Superclass & Object{T = ("123" : String)}
1

There are 1 answers

0
username On

I could not work around your given example, but this may work for your actual use case.

trait Superclass[T]

//The type of G
trait Foo:
  type Key
  type Mapping[K <: Key]

class A [K, KT <: K, M[_ <: K], G <: Foo {type Key = K; type Mapping[T <: K] = M[T]}] private(val key: KT, g: G)
    extends Superclass[M[KT]]

object A:
  def apply[G <: Foo](using g: G)(key: g.Key) =
     new A[g.Key, key.type, g.Mapping, g.type](key, g)

And you can use it like this:

class FooImpl extends Foo:
  type Key = String
  type Mapping[S <: String] = List[S]

given myG as FooImpl = FooImpl()

val x: Superclass[List["123"]] = A[FooImpl]("123")

In Foo, Key is used to bound what keys can be given to Mapping. The K parameter on class A is key's type's upper bound, while KT is the actual type of the key. M represents Mapping. I used a type refinement because type projections are unsound and have been dropped in Scala 3.

For convenience, you can add an apply method to A's companion and make A's own constructor private.

See it run in Scastie.

As for dependent types in constructors, see this issue. It looks like you can't use them the same way you can in methods.