【问题标题】:Elegant way to chain Scala partial functions链接 Scala 部分函数的优雅方式
【发布时间】:2016-12-07 10:27:12
【问题描述】:

我正在寻找一种优雅的方式来链接派生自公共基类型的部分函数。这个想法是每个部分函数都处理一个类型,因此它们可以很容易地组合成不同的类型,并且如果链接的部分函数未定义,则它们具有共同的包罗万象:

trait Message
trait SysMessage extends Message
trait UserMessage extends Message

case class TextSysMessage(m: String) extends SysMessage
case class TextUserMessage(m: String) extends UserMessage

class Test {

  type MessagePF = scala.PartialFunction[Message, String]
  type SysMessagePF = scala.PartialFunction[SysMessage, String]
  type UserMessagePF = scala.PartialFunction[UserMessage, String]

  def getSysMessage: SysMessagePF = {
    case sm: TextSysMessage ⇒ s"System message: ${sm.m}"
  }

  def getUserMessage: UserMessagePF = {
    case um: TextUserMessage ⇒ s"User message: ${um.m}"
  }

  def * : MessagePF = {
    case m ⇒ s"Unknown message: $m"
  }

  // Chained partials fails because `m` is a SysMessage with UserMessage
  def handler(m: Message): String = (getSysMessage orElse getUserMessage orElse *)(m)
}

显然,这种方法无法编译。我可以通过这样的嵌套模式匹配来解决这个问题

def getSysMessage: MessagePF = {
  case m: SysMessage ⇒ m match {
    case sm: TextSysMessage ⇒ s"System message: ${sm.m}"
  }
}

但后来我失去了处理所有未知消息的能力。有没有什么优雅的方法可以实现这个目标?

【问题讨论】:

  • 为什么不用Message 定义每个PartialFunction 作为输入类型?
  • 本质上是B计划......
  • 但是你怎么能期望一个带有具体子类型的部分函数可以与任意Message一起工作呢? def handler(m: Message): String = (getSysMessage)(m) 之类的东西也不起作用。
  • 没想到会这样。只是在寻找一种优雅的方式来实现类似的目标。
  • getSysMessagegetUserMessage 的全部目的是尝试在具体类型上匹配一些Message。如果您决定此部分函数必须采用 Message 的具体子类型,那么您已经(静态地)知道它属于这种类型,这与这些方法存在的意义不符。

标签: scala


【解决方案1】:

除了@adamwy + Hongxu Chen 的回答之外,您还可以定义自己的组合器,其中涉及隐式参数,因此强制执行略有不同的应用程序语法

implicit class PartFuncOps[A: ClassTag, B](pf: PartialFunction[A, B]) {
  def or[D >: A, C <: D : ClassTag](other: PartialFunction[C, B]): PartialFunction[D, B] = {
    case a: A if pf.isDefinedAt(a) ⇒ pf(a)
    case c: C if other.isDefinedAt(c) ⇒ other(c)
  }
}

现在你可以写了

def combine = getSysMessage or getUserMessage or *
def handler(m: Message): String = combine(m)

或者

def handler(m: Message): String = (getSysMessage or getUserMessage or *).apply(m)

【讨论】:

    【解决方案2】:

    按照@adamwy 的建议,您可以将偏函数类型更改为:

    type MessagePF = scala.PartialFunction[Message, String]
    type SysMessagePF = scala.PartialFunction[Message, String]
    type UserMessagePF = scala.PartialFunction[Message, String]
    

    【讨论】:

    • 或者只使用一种类型。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-13
    相关资源
    最近更新 更多