来自相同特征的案例类的模式匹配

时间:2018-09-07 10:57:42

标签: scala pattern-matching

所有人!

我在Scala模式匹配方面有一些问题。我正在为我的Web应用程序使用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-way中正确执行?谢谢。

3 个答案:

答案 0 :(得分: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))
}

答案 1 :(得分: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 :(得分: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))