scala3 icon indicating copy to clipboard operation
scala3 copied to clipboard

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

Open WojciechMazur opened this issue 6 months ago • 8 comments

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..

WojciechMazur avatar Aug 20 '24 16:08 WojciechMazur