2017-02-17 44 views
4

我正在嘗試編寫下面的驗證函數,以便驗證在遇到第一個錯誤後停止。返回類型three與其他功能不同。我爲了編譯這個代碼而使用哪個monad變換器?要使用哪種Monad變壓器?

import scalaz._ 
import Scalaz._ 
import scala.concurrent.Future 
import scala.concurrent.ExecutionContext.Implicits.global 


def one(a : String): Disjunction[Int, String] = 
    a == "one" match { 
    case true => \/-("one") 
    case false => -\/(2) 
    } 

def two(a : String): Disjunction[Int, String] = 
    a == "two" match { 
    case true => \/-("two") 
    case false => -\/(3) 
    } 

def three(a : String): Future[Disjunction[Int, String]] = 
    Future (a == "three") map { 
    case true => \/-("three") 
    case false => -\/(4) 
    } 

def validate(a : String) = for { 
    e1 <- one(a) 
    e2 <- two(a) 
    e3 <- EitherT(three(a)) 
} yield (e1 |+| e2 |+| e3) 

編譯錯誤:

Error:(27, 7) type mismatch; 
found : scalaz.EitherT[scala.concurrent.Future,Int,String] 
required: scalaz.\/[?,?] 
    e3 <- EitherT(three(a)) 
    ^
Error:(66, 7) type mismatch; 
found : scalaz.EitherT[scala.concurrent.Future,Int,String] 
required: scalaz.\/[?,?] 
    e3 <- EitherT(three(a)) 
    ^
+1

爲什麼你不會在'Future'或者''Wait''等待'three'結果中包含'one'和'two'? –

+0

你想看一下'EitherT' monad變換器,你的第三個'<-'沒有返回'String',它給了你整個'Disjunction',它可能不是你想要的(前兩個訪問'字符串「部分的析取)。 –

+0

@EndeNeu我試過EitherT,但仍然收到編譯錯誤。請參閱更新的問題以及編譯錯誤。我之前使用DisjunctionT(這只是EitherT的包裝),具有相同的結果。 –

回答

6

有你可以在這樣的情況下兩種常用的方法。首先是讓所有的方法返回你知道你將要處理的堆棧(在這種情況下爲EitherT[Future, Int, ?]),或者你可以讓每個單獨的方法返回最準確地捕獲它自己的效果的類型,然後提高你的值當你編寫它們時得到適當的。

第一種方法可以使語法更方便,如果您確切地知道該用法是什麼樣子,但後一種方法更靈活,並且在我看來通常是更好的選擇。在你的情況下,它會是這個樣子:

import scalaz._, Scalaz._ 
import scala.concurrent.Future 
import scala.concurrent.ExecutionContext.Implicits.global 

def one(a: String): Disjunction[Int, String] = (a == "one").either("one").or(2) 
def two(a: String): Disjunction[Int, String] = (a == "two").either("two").or(3) 

def three(a: String): EitherT[Future, Int, String] = EitherT(
    Future(a == "three").map(_.either("three").or(4)) 
) 

def validate(a: String) = for { 
    e1 <- EitherT.fromDisjunction[Future](one(a)) 
    e2 <- EitherT.fromDisjunction[Future](two(a)) 
    e3 <- three(a) 
} yield (e1 |+| e2 |+| e3) 

然後:

scala> validate("one").run.foreach(println) 
-\/(3) 

scala> validate("x").run.foreach(println) 
-\/(2) 

如果由於某種原因,你有一個普通的老Future,你想在for -comprehension使用,你可以用.liftM[EitherT[?[_], String, ?]]將其解壓到EitherT[Future, String, A]

(注意,此方法可能不是非常有用,因爲它永遠不會成功(一個字符串不能等於"one""two""three"在同一時間),但至少組成了工作。)

關於如何更普遍挑單子轉換堆棧:您剛剛打開內而外的類型,從而使Future[Disjunction[Int, ?]]成爲EitherT[Future, Int, ?]等。在這種情況下,具體而言,Future沒有一個單子轉換(這不是穿越和無法實現FutureT而無法阻止),所以無論如何你都知道它必須進入內部。