习惯上替代`if(x)Some(y)else None`

时间:2014-05-23 13:48:53

标签: scala functional-programming monads

我发现在我的代码中反复弹出以下模式,我的直觉说必须有一些惯用的Scala方法来更好地表达这个(Monadic或其他):

val someCollection: Seq[Thing] = ...
val makeBlah: Seq[Thing] => Blah = ...
...
if (someCollection.nonEmpty) Some(makeBlah(someCollection)) else None

更具体地说,我正在寻找与Option[T]相关的内容:

val someOption: Option[Thing] = ...
val makeBlah: Thing => Blah = ...
...
val result: Option[Blah] = someOption.map(makeBlah)

...但是评估语义基于某些谓词,而不是Some中的None / map模式匹配。

虽然上面的示例使用了一个集合 - 首先对它执行测试,然后可选地执行操作 - 但我并不是要暗示集合特定的用例。你可以想象Boolean被解除或强迫进入某个monad的情况:

val aThing: Thing = ...
val makeBlah: Thing => Blah = ...
val thingTest: Thing => Boolean ...
// theoretical
implicit def optionOnBoolean(b: Boolean): MonadOps[Option[Boolean]] = ... 
...
// NB: map could either have a Boolean parameter
//     that's always true, or be Unit.
//     Neither seem like good design 
val result: Option[Blah] = thingTest(aThing).map(makeBlah(aThing))

直观地说,这对我来说似乎是一个坏主意,因为它明确地分割了数据流,因为你没有任何东西可以通过map传递。

当寻找具有“monadic-like”行为但没有闭包来捕获数据的一般方法时,必须回答传递给map的内容以及它与谓词的连接的问题。这是我想到的构造类型:

val thing: Thing = ....
val makeBlah: Thing => Blah = ...
val thingTest: (Thing) => Boolean = ...
val result: Option[Blah] = WhenOption(thing, thingTest).map(makeBlah)

我的问题:Scala中是否已经存在某种东西,或者是否必须冒险去Scalaz才能获得这种构造?

或者是否存在其他习惯/习惯用法Scala?

修改:我的问题接近Scala - "if(true) Some(1)" without having to type "else None"但我希望解决在没有关闭的情况下实现它的问题。

3 个答案:

答案 0 :(得分:6)

为了完整性:

val someCollection: Seq[Thing] = ...
val makeBlah: Seq[Thing] => Blah = ...

您可以在Option上使用某些方法:

Some(someCollection).filterNot(_.isEmpty).map(makeBlah)

或理解

for(sc <- Some(someCollection) if !someCollection.isEmpty) yield makeBla(sc)

或作为模式匹配

someCollection match {
  case Seq() => None
  case x => Some(makeBlah(x))
}

但我认为if-then-else方法是最具可读性的方法。

答案 1 :(得分:3)

我会继续做你正在做的事情,除非你发现自己在相同的功能范围中重复同样的逻辑广告。它可读并且有意义。也就是说,如果你真的需要,你可以“解除”PartialFunction(见here):

def foo: PartialFunction[Seq[A], B]

def fooLifted: (Seq[A] => Option[B]) = foo.lift

现在你所要做的就是明确条件逻辑

def foo ={
  case seq if predicate(seq) => doStuff(seq)
}

这比你正在做的更多样板。

答案 2 :(得分:2)

FWIW,我提出同样的建议:

implicit class RichBoolean(val b: Boolean) extends AnyVal {
  def map[T](f: => T): Option[T] = if (b) Some(f) else None
  def flatMap[T](f: => Option[T]): Option[T] = if (b) f else None   
}

“地图”在这里感觉不对,但我想不出更好的事情。我非常喜欢这种结构,它可以帮助您在对数据进行连续几次操作时保持“流动”。