假设我有一个Cake
可以在多种状态之间循环:
sealed trait State extends Product with Serializable
object State {
final case object Raw extends State
final case class JustRight(temperature: Int) extends State
final case class Burnt(charCoalContent: Double) extends State
}
final case class Cake[S <: State](name: String, state: S)
这很好,因为现在我可以确保我只尝试输入 Raw
将蛋糕放入 toastr ,而不是立即食用。
但有时我只有 Cake[State]
躺在周围并想尝试吃它,但前提是它处于可食用状态。我当然可以始终在 cake.state
上进行模式匹配,但我认为应该可以通过添加方法 def narrow[S <: State]: Cake[State] => Option[Cake[S]]
来节省自己的几次击键次数.
但是,现在我正在努力实际实现该功能。编译器接受 Try(cake.asInstanceOf[Cake[S]]).toOption
,但似乎总是会成功(我猜是因为类型参数被删除,实际上任何类型 A
都会在这里被接受,而不仅仅是 S
)。似乎有效的是 Try(cake.copy(state = cake.state.asInstanceOf[S])).toOption
,但现在我已经制作了多余的数据副本。还有其他更好的方法吗?或者整个编码从一开始就有缺陷?
最佳答案
您可以使用类型类来解决此问题,该类型类(以类型安全的方式)检查并强制转换状态的类型。
sealed trait State extends Product with Serializable
object State {
final case object Raw extends State
type Raw = Raw.type
final case class JustRight(temperature: Int) extends State
final case class Burnt(charCoalContent: Double) extends State
sealed trait Checker[S <: State] {
def check(state: State): Option[S]
}
object Checker {
private def instance[S <: State](pf: PartialFunction[State, S]): Checker[S] =
new Checker[S] {
val f = pf.lift
override def check(state: State): Option[S] = f(state)
}
implicit final val RawChecker: Checker[Raw] = instance {
case Raw => Raw
}
implicit final val JustRightChecker: Checker[JustRight] = instance {
case s @ JustRight(_) => s
}
implicit final val BurntChecker: Checker[Burnt] = instance {
case s @ Burnt(_) => s
}
}
}
final case class Cake[S <: State](name: String, state: S)
def narrow[S <: State](cake: Cake[State])(implicit checker: State.Checker[S]): Option[Cake[S]] =
checker.check(cake.state).map(s => cake.copy(state = s))
你可以这样使用:
val rawCake: Cake[State] = Cake(name = "Foo", state = State.Raw)
narrow[State.Raw](rawCake)
// res: Option[Cake[State.Raw]] = Some(Cake(Foo,Raw))
narrow[State.JustRight](rawCake)
// res: Option[Cake[State.JustRight] = None
顺便说一句,如果您想避免复制
,您可以将check
更改为仅返回Boolean并使用脏asInstanceOf
.
// Technically speaking it is unsafe, but it seems to work just right.
def narrowUnsafe[S <: State](cake: Cake[State])(implicit checker: State.Checker[S]): Option[Cake[S]] =
if (checker.check(cake.state)) Some(cake.asInstanceOf[Cake[S]])
else None
(您可以看到正在运行的代码 here )
关于scala - 如何缩小状态机编码中上限类型参数的类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64715971/