【问题标题】:Scala: Does Option[Boolean] makes sense?Scala:Option[Boolean] 有意义吗?
【发布时间】:2014-08-07 17:39:47
【问题描述】:

所以Option[Int]Option[String] 或就此而言Option[A] 导致Some(A)None,但Boolean 是不同的,因为它本质上代表双重状态(true/false ),拥有Option[Boolean] 有意义吗?当JSON 响应不应该包含基于某些业务逻辑的布尔字段时,我经常会遇到这种情况。

有什么想法吗?

【问题讨论】:

  • 当然,这是三态的合理表示:真、假、不知道/不关心。
  • 可能不是性能最优的(因为它是一个成熟的类),但为什么不呢?如果需要优化,可以将其编码为Byte或Int,例如

标签: json api scala boolean option


【解决方案1】:

可选性是与您的数据类型相关的正交问题。所以是的,Option[Boolean]Option[Int] 一样有意义。

让我们谈谈您的特定用例。


如果业务规则规定字段(例如)isPrimeTime 必须是 Boolean 类型,但它是可选的,那么您应该使用 Option[Boolean] 对其进行建模。

None 在此上下文中表示缺少该值,Some(true) 表示“存在且为真”,Some(false) 表示“存在且为假”。这还允许您在代码中添加默认值,如下所示:

val isPrimeTime = json.getAs[Option[Boolean]]("isPrimeTime").getOrElse(false)

对于此类设置中出现的其他常见用例,您可以使用各种 Option 组合子。 (我会让你的想象力发挥作用。)


如果您的域有很多这样的“三态”字段,并且如果第三种状态表示一些特定于域的想法,从上下文中看不出来,我强烈建议您创建自己的 sum 类型。即使您在技术上仍然可以使用Option[Boolean],但这可能不利于您的理智。这是一个例子。

sealed trait SignalValueIndication
case class Specified(value: Boolean) extends SignalValueIndication
case object DontCare extends SignalValueIndication

// Use
val signalValue = signalValueIndication match {
  case Specified(value) => value
  case DontCare => chooseOptimalSignalValueForImpl
}

这似乎是一种巨大的浪费,因为您正在丢失Option 上可用的组合器。这是部分正确的。部分原因是由于这两种类型是同构的,所以编写桥不会那么难。

sealed trait SignalValueIndication {
  // ...
  def toOption: Option[Boolean] = this match {
    case Specified(value) => Some(value)
    case DontCare => None
  }

  def getOrElse(fallback: => Boolean) = this.toOption.getOrElse(fallback)
}

object SignalValueIndication {
  def fromOption(value: Option[Boolean]): SignalValueIndication = {
    value.map(Specified).getOrElse(DontCare)
  }
}

这仍然是重要的重复,您必须根据具体情况判断增加的清晰度是否弥补了这一点。

HaskellTips 最近在 twitter 上提出了类似的建议,随后进行了类似的讨论。你可以找到它here


有时,是否存在字段是您可以将其编码到数据结构中的决定。在这种情况下使用Option 是错误的。

这是一个例子。假设有一个字段differentiator 允许以下两种值。

// #1
{ "type": "lov", "value": "somethingFromSomeFixedSet" },

// #2
{ "type": "text", "value": "foo", "translatable": false }

在这里,假设translatable 字段是强制性的,但仅适用于`"type": "text"。

您可以使用Option 对其建模:

case class Differentiator(
  _type: DifferentiatorType, 
  value: String, 
  translatable: Option[Boolean]
)

但是,更好的建模方法是:

sealed trait Differentiator
case class Lov(value: String) extends Differentiator
case class Text(value: String, translatable: Boolean) extends Differentiator

Yaron Minsky 的 Effective ML 谈话中有一个部分对此进行了介绍。您可能会发现它很有帮助。 (虽然他使用 ML 来说明这些观点,但演讲很容易理解,也适用于 Scala 程序员)。

【讨论】:

    【解决方案2】:

    这是Option[Boolean] 的正确用法。假设我有一个正在运行的作业需要一些时间才能完成,并且我想要一个值来表示该作业是否成功。这表明Option[Boolean] 类型的值。

    • 工作完成之前不知道成功与否,所以有None的值。
    • 工作完成后,要么成功,要么失败,因此它具有Some(true)Some(false) 的价值。

    【讨论】:

    • 如果你能给Option[Option[Boolean]]举个例子,谢谢。
    • 对于任何X,您可能希望使用Option[X] 来指示X 是否可用。例如,假设您有一个检查作业是否成功的子任务;该子任务报告Option[Boolean] 以指示成功/失败/未完成,您可能返回Option[Option[Boolean]] 以指示子任务没有响应。最终您可能会将其展平,但最初您希望对任务失败和任务处理程序失败做出截然不同的响应。
    • @RexKerr 我的意思是幽默。 :) 但是一个很好的解释。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-11-25
    • 1970-01-01
    • 2010-11-27
    • 2021-06-20
    • 2016-09-16
    • 2010-09-23
    • 2011-10-04
    相关资源
    最近更新 更多