scala / bug

Scala 2 bug reports only. Please, no questions — proper bug reports only.
https://scala-lang.org
232 stars 21 forks source link

`orElse` method has incorrect signature #9301

Open scabug opened 9 years ago

scabug commented 9 years ago

The current definition is:

def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]): PartialFunction[A1, B1]

This prevents me from creating groups capturing sealed traits and chaining them:

sealed trait Special
case object One extends Special
case object Two extends Special

type Receive = PartialFunction[Any, Unit]

// compile error
def receive: Receive = handleAnyMessage orElse handleSpecialMessage 

def handleSpecialMessage: PartialFunction[Special, Unit] = {
  case One =>
  case Two =>
}

def handleAnyMessage: Receive = {
  case _ =>
}

I am using this pattern with actors to prevent myself from forgetting to implement handlers for messages.

A partial function should not get the most specific type. It should get the least specific type. After all, it has an isDefinedAt method.

My current workaround:

implicit def widenTypeOfPartialFunction[A : ClassTag, B, A1](p: PartialFunction[A, B]): PartialFunction[A1, B] =
    new Partial[A1, B] {
      def apply(a: A1): B =
        c match {
          case a: A => p(a)
        }

      def isDefinedAt(a: A1): Boolean =
        a match {
          case a: A => p isDefinedAt a
          case _ => false
        }
    }
scabug commented 9 years ago

Imported From: https://issues.scala-lang.org/browse/SI-9301?orig=1 Reporter: Erik Westra (eecolor) Affected Versions: 2.11.6

scabug commented 9 years ago

@Ichoran said: I agree that the logic is wrong. I think it's wrong because the type computation is hard to perform correctly, which is that you want A1 and A to intersect and be non-empty (unless A1 or A are Nothing). That is, you want the types to indicate that there's some hope to actually do something.

I'm not sure what the best fallback from that is, but I'm sure that insisting that A1 <: A, and then restricting the static type of the domain is wrong, since the original partial function might have perfectly well have handled some of the things in A - A1!

Unfortunately, these sorts of things are hard to change. I'm afraid you'll have to rely upon the workaround for a while.

dwijnand commented 5 years ago

Why isn't it, or "can't it be", A1 >: A?

In the specific case of Akka's actors, the modern solution is moving to akka-actor-typed!

joroKr21 commented 5 years ago

Because you can't pass a supertype to the original partial function. As the workaround shows you would need a ClassTag. Maybe one could use union types in Dotty somehow.