如何订购密封特征?

How to do ordering of a sealed trait?

我有一个由 "state machine" ( "flow chart" )

定义的分布式系统

每个系统在共享状态中写入状态 "log"

我将每个状态表示为密封特征的一部分,并为该状态"status"

我想"merge/reduce"到一个代表当前进度的状态。

(有一些放宽,因为并非所有人都必须成功才能成功完成最终状态)

有2个sealed traits代表流量:

sealed trait System
case object A extends System
case object B extends System
case object C extends System
...

sealed trait Status
case object Pending extends Status
case object InProgress extends Status
case object Success extends Status
case object Fail extends Status

日志:

A, Success
B, Fail
C, Pending
...
...

现在有一组规则可以用来定义单个状态降低

基本上它给出了优先级

A < B < C, ... < Z

Pending < InProgress < Success < Fail

所以如果状态为:

(A, Success) 对比 (C, Pending)

我想减少到(C,Pending)

如果

(A,Success) 对比 (B, Fail)

我想减少到(B, Fail)

在我的案例中,我可以将其建模为简单的整数比较(可能带有我明确测试的异常值)

我不清楚如何制作密封的特性comparible/orderable这会让我的生活更轻松

遵循这些原则就足够了:

def reduce(states: Seq[(System,Status)]) : (System,Status) = {
    states.order... {left.system < right.system) && (a.status < b.status) ... possibly another ordering test ....}.tail // take the last one in the ordering
}

你可以定义一个scala.math.Ordering[Status]:

object StatusOrdering extends Ordering[Status] {
  def compare(x: Status, y: Status): Int =
    (x, y) match {
      // assuming that the ordering is Pending < InProgress < Success < Fail...
      case (_, _) if (x eq y) => 0
      case (Pending, _) => -1
      case (_, Pending) => 1
      case (InProgress, _) => -1
      case (_, InProgress) => 1
      case (Success, _) => -1
      case (_, Success) => 1
      case _ => 0 // (Fail, Fail)
    }

在你的reduce中,你可以

import StatusOrdering.mkOrderingOps

你的 Status 对象将因 < 和朋友而丰富。

也可以让你的 trait 扩展 Ordered[Status],它定义了特征中的规范排序:

sealed trait OrderedStatus extends Ordered[OrderedStatus] {
  def compare(that: OrderedStatus): Int =
    (this, that) match {
      case (x, y) if (x eq y) => 0
      case (Qux, _) => -1
      case (_, Qux) => 1
      case (Quux, _) => -1
      case (_, Quux) => 1
      case _ => 0
    }
}

case object Qux extends OrderedStatus
case object Quux extends OrderedStatus
case object Quuux extends OrderedStatus

那么你不必导入 mkOrderingOps,但我个人不喜欢在 compare 方法中向前使用扩展 case objects(以及样板 compare 在每种情况下 object 更糟)。

一种方法是在 Map 秒内定义 SystemStatus 的优先级,然后通过 [=16] 定义 (System, Status) 的顺序=]:

val syMap: Map[System, Int] = Map(A->1, B->2, C->3)
val stMap: Map[Status, Int] = Map(Pending->1, InProgress->2, Success->3, Fail->4)

implicit val ssOrdering: Ordering[(System, Status)] =
  Ordering.by{ case (sy, st) => (syMap.getOrElse(sy, 0), stMap.getOrElse(st, 0)) }

import ssOrdering._

(A, Success) < (C, Pending)
// res1: Boolean = true

(A, Success) < (B, Fail)
// res2: Boolean = true

(C, Pending) < (B, Fail)
// res3: Boolean = false

请注意,在上面的示例代码中,不匹配的默认值 System/Status 设置为 0(最低优先级)。可以根据需要将它们设置为任何其他值。

减少 Seq(System, Status) 秒:

def ssReduce(ss: Seq[(System, Status)])(implicit ssOrd: Ordering[(System, Status)]) : (System, Status) = {
  import ssOrd._
  ss.reduce((acc, t) => if (t < acc) acc else t )  // Or simply `ss.max`
}

ssReduce(Seq((A, Success), (C, Pending), (B, Fail)))
// res4: (System, Status) = (C,Pending)

考虑 enumeratum-cats CatsOrderValueEnum 定义顺序的方法

import cats.Order
import enumeratum.values._
import cats.instances.int._

sealed abstract class Status(val value: Int) extends IntEnumEntry

object Status extends CatsOrderValueEnum[Int, Status] with IntEnum[Status] {
  case object Pending    extends Status(1)
  case object InProgress extends Status(2)
  case object Success  extends Status(3)
  case object Fail  extends Status(4)

  val values = findValues
}

object AdtOrder extends App {
  import Status._
  println(Order[Status].compare(Pending, Fail))
}

输出

-1

哪里

libraryDependencies ++= Seq(
  "com.beachape" %% "enumeratum" % "1.5.13",
  "com.beachape" %% "enumeratum-cats" % "1.5.15"
)