Scala:Option [Boolean]有意义吗?

时间:2014-06-17 15:59:28

标签: json api scala boolean option

因此,Option[Int]Option[String]Option[A]会产生Some(A)None,但Boolean会因此而异。固有地表示双重状态(true / false),有Option[Boolean]是否有意义?当JSON响应不应包含基于某些业务逻辑的布尔字段时,我会经常遇到这种情况。

有什么想法?

2 个答案:

答案 0 :(得分:10)

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

  • 在工作完成之前,我不知道它是否成功,因此它具有值None
  • 当工作完成后,它既可以成功,也可以不成功,因此它具有值Some(true)Some(false)

答案 1 :(得分:8)

选项性是您的数据类型的正交关注点。是的,Option[Boolean]Option[Int]一样有意义。

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


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

在此上下文中,

None表示缺少值,Some(true)表示“现在和真实”,Some(false)表示“存在和错误”。这也允许您在代码中添加默认值,如下所示:

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

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


如果你的域名中有很多这些“三态”字段,并且如果第三个状态表明某些特定于域的想法,那么从上下文中看不出这一点,我强烈建议你创建自己的总和类型。即使你在技术上仍然可以使用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程序员。)