2012-10-22 49 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