2016-03-15 175 views
2

序列我想更新Scala中的一個序列,我有這樣的代碼:斯卡拉檢查Eithers

def update(userId: Long): Either[String, Int] = { 
    Logins.findByUserId(userId) map { 
    logins: Login => update(login.id, 
     Seq(NamedParameter("random_date", "prefix-" + logins.randomDate))) 
    } match { 
    case sequence : Seq(Nil, Int) => sequence.foldLeft(Right(_) + Right(_)) 
    case _ => Left("error.logins.update") 
    } 
} 

findByUserId返回Seq[Logins]update返回Either[String, Int]其中Int是更新的行, 和數量String將是錯誤的描述。

我想要實現的是返回一個String,如果在更新列表時發生錯誤或者更新列的總數爲Int

的代碼不工作,我想我應該做的事情在match不同,我不知道我怎麼能檢查是否在EitherSeq在S的每一個元素是一個Right值。

+0

您似乎有兩個叫'update'的函數 - 這裏顯示的函數,然後是一個需要兩個參數的函數(第一個是'login.id'的第一個參數,第二個參數是' String')。這個函數返回什麼?此外,'login'來自哪裏 - 您傳遞給'map'的函數有一個輸入'login',而不是'login'。 – childofsoong

回答

1

如果您想提早退出,則不應該使用摺疊。更好的解決方案是遞歸迭代列表,更新和計數成功,然後在遇到錯誤時返回錯誤。

下面是一個顯示該技術的小示例函數。您可能需要修改此項,以便在每次登錄時執行更新,而不僅僅是計數。

val noErrors = List[Either[String,Int]](Right(10), Right(12)) 
val hasError = List[Either[String,Int]](Right(10), Left("oops"), Right(12)) 

def checkList(l: List[Either[String,Int]], goodCount: Int): Either[String, Int] = { 
    l match { 
    case Left(err) :: xs => 
     Left(err) 
    case Right(_) :: xs => 
     checkList(xs, (goodCount + 1)) 
    case Nil => 
     Right(goodCount) 
    } 

} 

val r1 = checkList(noErrors, 0) 
val r2 = checkList(hasError, 0) 

// r1: Either[String,Int] = Right(2) 
// r2: Either[String,Int] = Left(oops) 
+1

謝謝@justinhj,正如你所說我修改了我的代碼並完美工作。 – agusgambina

1

您想在更新失敗後立即停止,不是嗎? 這意味着你想要在map內部進行匹配,而不是在外部。 Try實際上是一個更適合此目的的構造,而不是Either。事情是這樣的,也許:

def update(userId: Long): Either[String, Int] = Try { 
    Logins.findByUserId(userId) map { login => 
    update(login.id, whatever) match { 
     case Right(x) => x 
     case Left(s) => throw new Exception(s) 
    } 
    }.sum 
} 
.map { n => Right(n) } 
.recover { case ex => Left(ex.getMessage) } 

BTW,約一階不太廣爲人知的事實是,把一個return聲明一個lambda裏面,其實從封閉方法返回。因此,另一個寫這個稍短的方法是這樣的:

def update(userId: Long): Either[String, Int] = 
    Logins.findByUserId(userId).foldLeft(Right(0)) { (sum,login) => 
    update(login.id, whatever) match { 
     case Right(x) => Right(sum.right + x) 
     case [email protected](s) => return error 
    } 
    } 

而且,爲什麼在世界上確實findUserById返回序列 ???

+0

謝謝你的回答。 findUserById返回一個Seq,因爲執行一個anorm選擇。 – agusgambina

1

如果您願意使用Scalaz或Cats,您可以使用traverse。使用Scalaz一個例子:

import scalaz.std.either._ 
import scalaz.std.list._ 
import scalaz.syntax.traverse._ 

val logins = Seq(1, 2, 3) 

val updateRight: Int => Either[String, Int] = Right(_) 
val updateLeft: Int => Either[String, Int] = _ => Left("kaboom") 

logins.toList.traverseU(updateLeft).map(_.sum) // Left(kaboom) 
logins.toList.traverseU(updateRight).map(_.sum) // Right(6) 

橫越越過登錄給我們帶來了Either[String, List[Int]],如果我們得到的List我們得到了想要Either[String, Int]的總和。

  • 我們使用toList因爲沒有爲Seq沒有Traverse實例。
  • traversemapsequence的組合。
  • 我們使用traverseU而不是traverse,因爲它推斷了我們的一些類型(否則我們應該引入一個類型別名或類型lambda)。
  • 因爲我們導入了scalaz.std.either._我們可以直接使用map而不使用正確的投影(.right.map)。
+0

謝謝@PeterNeyens是最優雅的解決方案,我會嘗試稍後實施 – agusgambina