2015-09-02 60 views
6

背景

我有Map[String,String]的配置值。我想提取一系列密鑰並提供有意義的錯誤消息,如果它們中的任何一個丟失的話。例如:使用| @ |進行驗證在斯卡拉

val a = Map("url"->"http://example.com", "user"->"bob", "password"->"12345") 

說我要變成一個案例類這樣的:

case class HttpConnectionParams(url:String, user:String, password: String) 

現在,我可以簡單地用一個for循環提取值:

for(url <- a.get("url"); 
    user <- a.get("user"); 
    password <- a.get("password")) yield { 
    HttpConnectionParams(url,user,password) 
} 

要獲得Option[HttpConnectionParams]。這是很好,乾淨,除非我得到一個None然後我不知道什麼是失蹤。我想提供這些信息。

驗證與斯卡拉

輸入斯卡拉。我正在使用版本7.1.3。

從我已經能夠放在一起(一個很好的參考是here),我可以使用析取:

for(url <- a.get("url") \/> "Url must be supplied"; 
    user <- a.get("user") \/> "Username must be supplied"; 
    password <- a.get("password") \/> "Password must be supplied") yield { 
    HttpConnectionParams(url,user,password) 
} 

這是不錯的,因爲現在我得到一個錯誤信息,但這是railway oriented因爲它在第一次失敗時停止。如果我想要得到所有的錯誤怎麼辦?讓我們用驗證和應用型製造商(又名 「| @ |」):

val result = a.get("url").toSuccess("Url must be supplied") |@| 
      a.get("username").toSuccess("Username must be supplied") |@| 
      a.get("password").toSuccess("Password must be supplied") 

result.tupled match { 
    case Success((url,user,password)) => HttpConnectionParams(url,user,password) 
    case Failure(m) => println("There was a failure"+m) 
} 

問題

這做什麼,我希望,但我瞭解使用說明一些問題:

  • 是這個用例有一個簡單的替代scalaz的選擇嗎?如果我不需要,我寧願不打開潘多拉的盒子並引入scalaz。
  • 我不想使用scalaz的一個原因是,如果你不像我一樣知道整個框架,那麼真的很難弄清楚該怎麼做。例如,您需要獲得上述代碼才能工作的implicits列表是什麼?不知何故,我不適合我[1]。我如何從API文檔中弄清楚這一點?
  • 有更簡潔的方式來表達驗證用例嗎?我偶然發現,直到我找到了一些有效的工具,我不知道是否有其他更好的方法可以在scalaz中做同樣的事情。

[1]經過很多驚愕之後,我到達這組適用用例的進口套件。希望這有助於人:

import scalaz.std.string._ 
import scalaz.syntax.std.option._ 
import scalaz.syntax.apply._ 
import scalaz.Success 
import scalaz.Failure 
+0

「我不想使用scalaz的一個原因是,如果你不像我一樣知道整個框架,真的很難弄清楚該怎麼做」 - 我不會直接告訴Tony莫里斯;)。斯卡拉茲可能不是每個人的一杯茶;如果您不是硬核FP的說服力和/或不熟悉Haskell(需要從該語言中獲取很多靈感),那麼需要學習一個陡峭的學習曲線。這是一個功能強大的圖書館,如果你知道如何處理它 - 我得到的印象是你敲開它,因爲你不能完全學習它 - 這不是斯卡拉斯的錯 –

+0

斯卡拉斯的替代品是貓。其中一個目標是在文檔和示例方面比Scalaz更加開放和用戶友好,儘管我發現Scalaz社區隨時準備幫助解決我遇到的任何問題。 –

+0

是的,一位同事推薦Cat,但版本爲0.2,是否可以讓人們普遍使用它? –

回答

13

可以很好地做到這多一點通過定義一個輔助方法和跳過.tupled一步使用.apply

import scalaz._, Scalaz._ 

def lookup[K, V](m: Map[K, V], k: K, message: String): ValidationNel[String, V] = 
    m.get(k).toSuccess(NonEmptyList(message)) 

val validated: ValidationNel[String, HttpConnectionParams] = (
    lookup(a, "url", "Url must be supplied") |@| 
    lookup(a, "username", "Username must be supplied") |@| 
    lookup(a, "password", "Password must be supplied") 
)(HttpConnectionParams.apply) 

而且,請不要感到羞愧使用import scalaz._, Scalaz._。我們都這樣做,在絕大多數情況下都很好。您可以隨時返回並稍後優化您的導入。我還支持this answer我幾年前寫過一篇文章 - 爲了能夠有效地使用它,您不應該覺得需要全面瞭解Scalaz(或貓)。

+2

只要注意到這不是對你的第一個問題的真正答案,但是現在沒有正確的偏見,在標準庫中積累析取類型,所以除非你想自己推出,否則貓或斯卡拉茲(或斯卡拉奇)是你最好的選擇。 –

+0

你回答了我沒有問過的問題,這是使用非空列表類(ValidationNel等)。這是爲了避免跟隨任何人的簡單串聯多個錯誤消息。謝謝,這非常有幫助。 –