scala3
scala3 copied to clipboard
No workaround to dependent-type match types after SIP-56
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..