Skip to content

No workaround to dependent-type match types after SIP-56 #21402

Closed
@WojciechMazur

Description

@WojciechMazur

Based on the problem found in akka/akka and apache/pekko

In the snippet we can come up with workaround to TypedMultiMap.get by using transparent inline however there seems to be no workaround for the TypedMultiMap.inserted method.

Compiler version

All Scala 3.4+ versions

Minimized code

abstract class AbstractServiceKey:
  type Protocol

abstract class ServiceKey[T] extends AbstractServiceKey:
  type Protocol = T

type Aux[P] = AbstractServiceKey { type Protocol = P }
type Service[K <: Aux[?]] = K match
  case Aux[t] => ActorRef[t]
type Subscriber[K <: Aux[?]] = K match
  case Aux[t] => ActorRef[ReceptionistMessages.Listing[t]]

trait ActorRef[-T]

object ReceptionistMessages:
  final case class Listing[T](key: ServiceKey[T])

class TypedMultiMap[T <: AnyRef, K[_ <: T]]:
  def get(key: T): Set[K[key.type]] = ???
  transparent inline def getInlined(key: T): Set[K[key.type]] = ???
  inline def inserted(key: T, value: K[key.type]): TypedMultiMap[T, K] = ???

object LocalReceptionist {
  final case class State(
      services: TypedMultiMap[AbstractServiceKey, Service],
      subscriptions: TypedMultiMap[AbstractServiceKey, Subscriber]
  ):
    def testInsert(key: AbstractServiceKey)(serviceInstance: ActorRef[key.Protocol]): State = {
      val fails = services.inserted(key, serviceInstance) // error
      ???
    }

  def testGet[T](key: AbstractServiceKey): Unit = {
    val newState: State = ???
    val fails: Set[ActorRef[key.Protocol]] = newState.services.get(key) // error
    val works: Set[ActorRef[key.Protocol]] = newState.services.getInlined(key) // workaround

    val fails2: Set[ActorRef[ReceptionistMessages.Listing[key.Protocol]]] = newState.subscriptions.get(key) // error
    val works2: Set[ActorRef[ReceptionistMessages.Listing[key.Protocol]]] = newState.subscriptions.getInlined(key) // workaround
  }
}

Output

[error] ./test.scala:29:42
[error] Found:    (serviceInstance : ActorRef[key.Protocol])
[error] Required: Service[(key : AbstractServiceKey)]
[error] 
[error] Note: a match type could not be fully reduced:
[error] 
[error]   trying to reduce  Service[(key : AbstractServiceKey)]
[error]   failed since selector (key : AbstractServiceKey)
[error]   does not uniquely determine parameter t in
[error]     case Aux[t] => ActorRef[t]
[error]   The computed bounds for the parameter are:
[error]     t
[error]       val fails = services.inserted(key, serviceInstance) // error
[error]                                          ^^^^^^^^^^^^^^^
[error] ./test.scala:35:46
[error] Found:    Set[Service[(key : AbstractServiceKey)]]
[error] Required: Set[ActorRef[key.Protocol]]
[error] 
[error] Note: a match type could not be fully reduced:
[error] 
[error]   trying to reduce  Service[(key : AbstractServiceKey)]
[error]   failed since selector (key : AbstractServiceKey)
[error]   does not uniquely determine parameter t in
[error]     case Aux[t] => ActorRef[t]
[error]   The computed bounds for the parameter are:
[error]     t
[error]     val fails: Set[ActorRef[key.Protocol]] = newState.services.get(key) // error
[error]                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^
[error] ./test.scala:38:77
[error] Found:    Set[Subscriber[(key : AbstractServiceKey)]]
[error] Required: Set[ActorRef[ReceptionistMessages.Listing[key.Protocol]]]
[error] 
[error] Note: a match type could not be fully reduced:
[error] 
[error]   trying to reduce  Subscriber[(key : AbstractServiceKey)]
[error]   failed since selector (key : AbstractServiceKey)
[error]   does not uniquely determine parameter t in
[error]     case Aux[t] => ActorRef[ReceptionistMessages.Listing[t]]
[error]   The computed bounds for the parameter are:
[error]     t
[error]     val fails2: Set[ActorRef[ReceptionistMessages.Listing[key.Protocol]]] = newState.subscriptions.get(key) // error
[error]                                         

Expectation

This kind of issues might pop-up more often as projects would migrate to Scala 3.4+. It would be great if we could propose some workarounds that would allow for their compilation..

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions