2016-12-18 25 views
0

https://gist.github.com/satyagraha/897e427bfb5ed203e9d3054ac6705704我已經發布了一個看起來合理的Scala貓驗證方案,但我還沒有找到一個非常整潔的解決方案。在斯卡拉貓驗證,如何可以結合訂購的驗證

本質上,有兩個階段的驗證,其中個別字段驗證,然後調用一個類構造函數,可能會由於內部檢查而拋出(一般來說,這可能不是我的控制權改變,因此異常處理碼)。如果任何字段驗證失敗,我們希望不要調用構造函數,而且還要將任何構造函數失敗合併到最終結果中。 「快速失敗」絕對就在這裏進行兩階段檢查。

這是一種flatMap問題,其中cats.data.Validated框架似乎通過cats.data.Validated#andThen操作處理。但是,我無法找到一個特別簡潔的解決方案,正如您在代碼中看到的那樣。 cats.syntax.CartesianBuilder有相當數量的操作可用,我不清楚如何將其與andThen操作鏈接。

任何想法歡迎!請注意,有一個貓問題https://github.com/typelevel/cats/issues/1343這可能是相關的,不確定。

回答

1

我會做一個輔助二次函數來包裝異常拋出的:

def attempt[A, B](f: A => B): A => Validated[Message, B] = a => tryNonFatal(f(a)) 

此外,case類的默認同伴擴展功能N特質,所以沒有必要做(User.apply _).tupled ,它可以縮短到User.tupled(自定義的同伴,你需要寫extends ((...) => ...))apply倍率會自動生成)

因此,我們最終與使用andThen

val valids = validateName(nameRepr) |@| validateDate(dateDepr) 
val res: Validated[Message, User] = valids.tupled andThen attempt(User.tupled) 
+0

這看起來像一個很好的解決方案,並且可以擴展到更一般的情況。謝謝! – satyagraha

1

對於快速失敗,鏈式驗證使用起來更容易EitherValidated。您可以輕鬆地從Either切換到Validated,反之亦然,具體取決於是否需要錯誤累積。

解決您的問題的一個可能的方法是創建一個User的智能構造函數,它將返回Either[Message, User]並將其與Validated[Message, (Name, Date)]一起使用。

import cats.implicits._ 
import cats.data.Validated 

def user(name: Name, date: Date): Either[Message, User] = 
    Either.catchNonFatal(User(name, date)).leftMap(Message.toMessage) 

// error accumulation -> Validated 
val valids: Validated[Message, (Name, Date)] = 
    (validateName(nameRepr) |@| validateDate(dateDepr)).tupled 

// error short circuiting -> either 
val userOrMessage: Either[Message, User] = 
    valids.toEither.flatMap((user _).tupled) 

// Either[Message,User] = Right(User(Name(joe),Date(now)))