从相同特征派生的案例类的模式匹配

问题描述 投票:2回答:3

大家!

我对Scala模式匹配有一点问题。我在我的网络应用程序中使用Korolev框架,但我敢打赌Scala中的问题更深入。

我有所有州的基本特征:

trait BaseState {
  val notifications: List[Notification]
}

两个派生的案例类:

case class GuestState(
                       notifications: List[Notification] = List(),
                       isRegistration: Boolean = false
                     ) extends BaseState

case class AuthenticatedState(
                          notifications: List[Notification] = List(),
                          user: UserWithPermissions
                        ) extends BaseState

在一个事件处理程序中,我需要在没有特定通知的情况下获得类似状态。现在它的工作原理如下:

event('click) { access =>
              access.transition {
                case s: GuestState => s.copy(notifications = notifications.filter(x => x != n))
                case s: AuthenticatedState => s.copy(notifications = notifications.filter(x => x != n))
              }
          }

对于这两种类型,我必须完全复制代码,因为BaseState没有copy()方法,编译器因错误而失败。

我怎样才能以scala方式正确地完成它?谢谢。

scala pattern-matching
3个回答
2
投票

这两个案例实际上并没有做同样的事情,因为两个copy方法有不同的签名。因此虽然它可能在文本上是相同的,但代码实际上在每种情况下都做了不同的事情。

这里的一个选择是向特征添加一个抽象的filterNotifications方法并调用:

trait BaseState {
  val notifications: List[Notification]
  def filterNotifications(f: Notification => Boolean): BaseState
}

case class GuestState(
  notifications: List[Notification] = List(),
  isRegistration: Boolean = false
) extends BaseState {
  def filterNotifications(f: Notification => Boolean): BaseState =
    this.copy(notifications=notifications.filter(f))
}

case class AuthenticatedState(
  notifications: List[Notification] = List(),
  user: UserWithPermissions
) extends BaseState {
  def filterNotifications(f: Notification => Boolean): BaseState =
    this.copy(notifications=notifications.filter(f))
}

2
投票

我过去有过类似的场景,但从未找到过一个非常优雅的解决方案,因为具有相同或相似签名的复制方法不能以多态方式轻松表达。

我通常在特质和案例类中加入一些管道,以便重复至少在一个地方:

trait BaseState {
  type ThisType <: BaseState
  val notifications: List[Notification]
  def withNotifications(notifications: List[Notification]): ThisType
}

case class GuestState(notifications: List[Notification] = List(),
                    isRegistration: Boolean = false) extends BaseState {
  type ThisType = GuestState
  def withNotifications(notifications: List[Notification]): ThisType = 
    copy(notifications = notifications)
}

case class AuthenticatedState(notifications: List[Notification] = List(),
                            user: UserWithPermissions) extends BaseState {
  type ThisType = AuthenticatedState
  def withNotifications(notifications: List[Notification]): ThisType = 
    copy(notifications = notifications)
}

这远非理想,也许有更好的模式。但是如果你像你的例子中那样使用逻辑很多,它至少会显着减少样板。如果还重复使用过滤器逻辑,则可以向基本特征添加更具体的filterNotififications

与其他答案相比,此解决方案的优势在于您在过滤时不会丢失类型精度。

并记住尽可能密封特性。


2
投票

如果不改变基础特性,它是不可解决的。当编译器显然没有它时,编译器不允许你对特性执行操作copy

一种方法是在BaseState上指定映射/过滤功能,如其他答案所示。

另一种方法是用组合替换继承:

case class Notified(state: BaseState, notifications: List[Notification] = List())

sealed trait BaseState
case class GuestState(isRegistration: Boolean = false) extends BaseState 
case class AuthenticatedState(user: UserWithPermissions) extends BaseState

val notified = Notified(GuestState())
val result = notified.copy(notifications = notifications.filter(_ => true))
© www.soinside.com 2019 - 2024. All rights reserved.