2012-10-22 48 views
3

我已经发布了很多关于Scala故障处理的问题,我非常感谢大家的回答。返回类型为Option时处理故障快速故障[错误]

我明白我的选项可以用和Scalaz或理解力打交道时,我有另一个(持续多久?)问题:

怎么办时,操作处理的操作的快速失败序列在非功能性世界之外,像数据库一样?


我的意思是我有一个这样的方法:

def insertItem(item: Item): Either[Error,Item] 

由于无论是和这些答案,我知道如何与要么做到这一点:Chaining method calls with EitherMethod parameters validation in Scala, with for comprehension and monads

但我Item case类是不可变的,因为调用者已经具有该值,所以将它作为Right返回并没有什么意义。

因此我该怎么办的事情相同的一种:

def insertItem(item: Item): Option[Error] 

在我的应用程序,创建一个用户的时候,我们也创造了一些物品给他。 如果一个项目无法创建,那么整个过程应该停止。

当我直接使用Option[Error]来理解,我不认为我会得到我期望的结果。

我想这是有道理的做这样的事情:

for { 
    _ <- insertItem(item1).toLeft("???").right 
    _ <- insertItem(item2).toLeft("???").right 
    _ <- insertItem(item3).toLeft("???").right 
} 

但随着价值观“???”我把我的权利没有用处,我想我错过了优雅的解决方案,不涉及创建永远不会使用的权利。

我觉得我在寻找的东西只有在结果为None时才会被理解,这有点奇怪,因为我只是想继续下一个操作,而不是做一个真正的map操作。如果可能的话,我希望非Scalaz和Scalaz解决方案。 我不确定Scalaz如何处理这些事情,因为它似乎更专注于真正的函数式编程,也许不会像我的用例那样为副作用行为提供代码?

回答

7

我没有看到一个原则性的理由,不要在这样的情况下使用Either[Error, Unit](或者至少我已经做到了,而且我对此不感到内疚)。假设我们有以下几点:

def launch(thing: String): Either[String, Unit] = Either.cond(
    thing.nonEmpty, 
    println("Launching the " + thing), 
    "Need something to launch!" 
) 

我们可以证明,右投影单子是适当地懒:

scala> for { 
    | _ <- launch("flowers").right 
    | _ <- launch("").right 
    | r <- launch("MISSILES").right 
    | } yield r 
Launching the flowers 
res1: Either[String,Unit] = Left(Need something to launch!) 

没有得到导弹发射,根据需要。


值得一提的是,如果你使用Option而不是Either,您所描述的操作仅仅是赋予了“第一”幺实例Option总和(这里的加法运算只是orElse)。例如,我们可以写与Scalaz 7如下:

import scalaz._, Scalaz._ 

def fst[A](x: Option[A]): Option[A] @@ Tags.First = Tag(x) 

def launch(thing: String): Option[String] = if (thing.isEmpty) Some(
    "Need something to launch!" 
) else { 
    println("Launching the " + thing) 
    None 
} 

现在:

scala> val r: Option[String] = fst(launch("flowers")) |+| fst(
    | launch("")) |+| fst(launch("MISSILES")) 
Launching the flowers 
r: Option[String] = Some(Need something to launch!) 

再次,没有导弹。

+0

看起来不错,谢谢没有一个合适的单位 –

0

如果你只在第一次失败感兴趣,比Iterator可以是你的选择:

case class Item(i: Int) 

case class Error(i: Item) 

val items = Iterator(1,3,7,-5,-2,0) map Item 

def insertItem(item: Item): Option[Error] = 
    if (item.i < 0) Some(Error(item)) else None 

scala> val error = (items map insertItem find (_.isDefined)).flatten 
error: Option[Error] = Some(Error(Item(-5))) 

如果insertItem不是你唯一的方法,可以给他们打电话与自己的价值观分开:

val items = Iterator(
() => insertItem(i1), 
() => deleteItem(i2), 
() => insertItem(i3), 
() => updateItem(i4)) 
1

如果你想链一起包含Option[Error]和只执行,如果OptionNone,您可以使用orElse为下一步的方法。

+0

这是我没有,虽然关于一个不错的主意。它适用于以下操作的懒惰评估。唯一的问题是奇怪的可读性 –

2

我们做同样的事情,我与scalaz工作快速失败有消息发送,尽管这不是地道的scalaz:

def insertItem(item: Item): Validation[Error, Unit] 

val result = for { 
    _ <- insertItem(item1) 
    _ <- insertItem(item2) 
    _ <- insertItem(item3) 
} yield Unit 
+0

我认为斯卡拉斯验证是一个错误列表,而不仅仅是一个错误,它不是快速正确的? –

+2

@SebastienLorber验证的flatMap方法是快速失败的。应用实例累计。 – stew

+0

也有Scalaz Either类型('\ /'),它只是'scala.Either'更合理的版本。 – Hugh