在我的应用程序的某个地方,我Either ParserError MyParseResult从Parsec 收到一个.在下游,这个结果可以使用其他库完成其他一些解析.在第二阶段的解析过程中,也可能会出现某种错误,我希望将其作为a传递Left String,但为此我需要将结果从Parsec转换为String.为了实现这一点,我需要一个允许我Left使用show函数映射a 的函数.
我想的映射函数看起来像这样:
mapLeft :: (a -> b) -> Either a c -> Either b c
mapLeft f (Left x) = Left $ f x
mapLeft _ x = x
Run Code Online (Sandbox Code Playgroud)
但我很惊讶没有发现任何与hackage db相匹配的东西.所以现在我怀疑我是否正在使用正确的方法解决我的问题.
为什么标准库中没有这样的功能?我的做法有什么问题?
除了使用之外match,是否有类似于选项的方式来getOrElse实现Right或者Left值的实际内容?
scala> val x: Either[String,Int] = Right(5)
scala> val a: String = x match {
case Right(x) => x.toString
case Left(x) => "left"
}
a: String = 5
Run Code Online (Sandbox Code Playgroud) 假设我想编写一个带有以下签名的方法:
def parse(input: List[(String, String)]):
ValidationNel[Throwable, List[(Int, Int)]]
Run Code Online (Sandbox Code Playgroud)
对于输入中的每对字符串,它需要验证两个成员都可以解析为整数,并且第一个成员小于第二个.然后它需要返回整数,累积任何出现的错误.
首先,我将定义一个错误类型:
import scalaz._, Scalaz._
case class InvalidSizes(x: Int, y: Int) extends Exception(
s"Error: $x is not smaller than $y!"
)
Run Code Online (Sandbox Code Playgroud)
现在我可以按如下方式实现我的方法:
def checkParses(p: (String, String)):
ValidationNel[NumberFormatException, (Int, Int)] =
p.bitraverse[
({ type L[x] = ValidationNel[NumberFormatException, x] })#L, Int, Int
](
_.parseInt.toValidationNel,
_.parseInt.toValidationNel
)
def checkValues(p: (Int, Int)): Validation[InvalidSizes, (Int, Int)] =
if (p._1 >= p._2) InvalidSizes(p._1, p._2).failure else p.success
def parse(input: List[(String, String)]):
ValidationNel[Throwable, List[(Int, Int)]] = input.traverseU(p =>
checkParses(p).fold(_.failure, checkValues …Run Code Online (Sandbox Code Playgroud) 我想拆分List[Either[A, B]]两个列表.
有没有更好的办法 ?
def lefts[A, B](eithers : List[Either[A, B]]) : List[A] = eithers.collect { case Left(l) => l}
def rights[A, B](eithers : List[Either[A, B]]) : List[B] = eithers.collect { case Right(r) => r}
Run Code Online (Sandbox Code Playgroud) 如果是scala函数
def A(): Either[Exception, ArrayBuffer[Int]] = {
...
}
Run Code Online (Sandbox Code Playgroud)
什么应该是处理返回结果的正确方法?
val a = A()
和?
我希望从返回类型的方法中轻松提取值Either<Exception, Object>。
我正在做一些测试,但无法轻松测试我的方法的返回。
例如:
final Either<ServerException, TokenModel> result = await repository.getToken(...);
Run Code Online (Sandbox Code Playgroud)
为了测试我能够做到这一点
expect(result, equals(Right(tokenModelExpected))); // => OK
Run Code Online (Sandbox Code Playgroud)
现在如何直接检索结果?
final TokenModel modelRetrieved = Left(result); ==> Not working..
Run Code Online (Sandbox Code Playgroud)
我发现我必须这样投:
final TokenModel modelRetrieved = (result as Left).value; ==> But I have some linter complain, that telling me that I shouldn't do as to cast on object...
Run Code Online (Sandbox Code Playgroud)
我也想测试异常但它不起作用,例如:
expect(result, equals(Left(ServerException()))); // => KO
Run Code Online (Sandbox Code Playgroud)
所以我试过这个
expect(Left(ServerException()), equals(Left(ServerException()))); // => KO as well, because it says that the instances are different.
Run Code Online (Sandbox Code Playgroud) 我的印象是,某个地方有一个实例,但我似乎无法找到它.我已经尝试导入Control.Monad,Control.Monad.Instances和Data.Either,如图所示
module Main where
import Control.Monad
import Data.Either
import Control.Monad.Instances
test :: [Either a b] -> Either a [b]
test = sequence
main = return ()
Run Code Online (Sandbox Code Playgroud)
但ghc告诉我它无法演绎(Monad(Either a)).添加
instance Monad (Either a) where
return = Right
Right b >>= f = f b
Left a >>= _ = Left a
Run Code Online (Sandbox Code Playgroud)
使代码编译,但是这个实例声明似乎很普遍,如果它在某些标准模块中还没有出现,那对我来说没有意义.如果是的话,我应该在哪里找到它,如果不是,那么有没有理由呢?
--------------编辑---------------
请注意,我现在认为下面的user31708的答案("从4.6开始,实例在Data.Either本身.")是目前正确的答案.我不确定在这种情况下重新分配所选答案的正确协议,其中所选答案是在提出问题时的正确答案,所以我保持原样.如果还有其他指导方针,请纠正我.
我想知道我的方法应该是什么签名,以便我优雅地处理不同类型的失败.
这个问题在某种程度上是我已经在Scala中处理错误的许多问题的总结.你可以在这里找到一些问题:
现在,我理解以下内容:
存储库层
现在请考虑我有一个UserRepository.所述UserRepository存储的用户,并限定了findById方法.可能发生以下故障:
OutOfMemoryError)此外,用户可能会丢失,从而导致Option[User]结果
使用存储库的JDBC实现,可以抛出SQL,非致命异常(约束违规或其他),因此使用Try是有意义的.
当我们处理IO操作时,如果我们想要纯函数,那么IO monad也是有意义的.
所以结果类型可能是:
Try[Option[User]]IO[Option[User]]服务层
现在让我们介绍一个业务层,UserService它提供了一些updateUserName(id,newUserName)使用先前定义findById的存储库的方法.
可能发生以下故障:
然后结果类型可以是:
Try[Either[BusinessError,User]]IO[Either[BusinessError,User]]这里的BusinessError不是Throwable,因为它不是一个例外的失败.
使用for-comprehensions
我想继续使用for-comprehensions来组合方法调用.
我们不能轻易地将不同的monad混合起来进行理解,所以我想我的所有操作都应该有一些统一的返回类型吗?
我只是想知道你在现实世界的Scala应用程序中如何成功地在不同类型的故障发生时继续使用for-understanding.
对于现在来说,for-comprehension对我来说很好,使用服务和存储库都可以返回,Either[Error,Result]但是所有不同类型的故障都会融合在一起,并且处理这些故障会变得很糟糕.
您是否定义了不同类型的monad之间的隐式转换,以便能够使用for -reherehension?
你定义自己的monad来处理失败吗?
顺便说一下,我很快就会使用异步IO驱动程序.所以我想我的返回类型可能更复杂:IO[Future[Either[BusinessError,User]]]
任何建议都会受到欢迎,因为我真的不知道该使用什么,而我的应用程序并不花哨:它只是一个API,我应该能够区分可以向客户端显示的业务错误,以及技术错误.我试图找到一个优雅而纯粹的解决方案.
我正在玩Maybe和Either monad类型(链接,根据返回值应用条件函数,还返回链接函数失败的错误消息等).所以我觉得我们可以通过使用Either monad来实现相同和更多的事情.那么我的问题是那些之间的实际或概念上的区别?
我知道现在很难在不破坏现有代码的情况下进行更改,但我想知道为什么首先要这样做。
为什么不只是:
sealed trait Either[+A, +B]
case class Left[A](x: A) extends Either[A, Nothing]
case class Right[B](x: B) extends Either[Nothing, B]
Run Code Online (Sandbox Code Playgroud)
这里是否有一些我没有看到的缺点......?