質問

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?

役に立ちましたか?

解決

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.

他のヒント

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.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top