Reputation: 12565
I would like to define a class hierarchy of classes that handle messages using a partial function. Each class should defer messages that it can not handle to its superclass. Something like this:
type Respond = PartialFunction[String, String]
abstract class HandlesNothing {
def respond: Respond = Map.empty // the empty response. Handles nothing
}
class HandlesA extends HandlesNothing {
override def respond = { case "a" => "The letter A" } orElse super.respond
}
class HandlesAAndB extends HandlesA {
override def respond = { case "b" => "The letter B" } orElse super.respond
}
You get the idea. But when I try this I get the following error
<console>:21: error: missing parameter type for expanded function
The argument types of an anonymous function must be fully known. (SLS 8.5)
Expected type was: ?
override def respond = { case "b" => "The letter B" } orElse super.respond
^
When I give the type of the {...} block explicitly, it works:
class HandlesAAndB extends HandlesA {
override def respond = ({ case "b" => "The letter B" }:Respond) orElse super.respond
}
Is there a way to achieve this without having to explicitly specify the type of the partial function block?
Upvotes: 0
Views: 644
Reputation: 167901
The problem is that the type of the left-hand side of the orElse
is inadequately specified by the return type of the method given that the right-hand side exists. (Also, the LHS is not by syntax necessarily a partial function; it may be an incompletely specified Function1
.) Since even conceptually the compiler can't really do the right thing without a bit of extra help here, you're going to have to do something.
So the only question left is whether there is a better way than : Respond
to indicate the type of the left-hand side. It turns out that the answer is maybe:
implicit class Resp(val underlying: Respond) extends AnyVal {
def or(r: Respond) = underlying orElse r
}
abstract class A { def respond: Respond = Map.empty }
class B extends A {
override def respond = Resp{ case "A" => "The letter A" } or super.respond
}
Thanks to the use of implicit value classes, there's essentially no overhead for doing it this way. The cost is that you have a custom mini-DSL instead of vanilla Scala library calls.
Upvotes: 2
Reputation: 14842
Your could do:
abstract class HandlesNothing {
def respond: Respond = Map.empty // the empty response. Handles nothing
protected def c(supPf: Respond)(pf: Respond) = pf orElse supPf
}
class HandlesA extends HandlesNothing {
override def respond = c(super.respond) { case "a" => "The letter A" }
}
It's admittedly ugly, but saves you typing.
Interesting detail found during exploration: Impossible to bind to super
of this
. That would have been really cool.
Upvotes: 1