我想知道使用monads编写代码的最佳方法是什么(Option,Try,Either)。 我的第一印象是那些monad应该让我写逻辑并忽略错误,然后根据类型结果我会更好地知道发生了什么。 我举一个例子
val a:Option[Something] = list.get("key")
现在我可以操作我的价值,就像我用map和flatMap一样 在代码的最后,如果我有None,则意味着列表没有“密钥”。 此工作流程仅在您具有小逻辑时才有效。 但如果我有很多逻辑,我应该如何编写代码 即:
val server:Option[Server] = serverList.get("serverId")
val value:Option[Try[Value]] = serverList.map(serverId=>getDataFromServer(serverId))
val processedValue:Option[Try[Some[OtherValue]]] = value.map(server => server.map( value=> processValue(value))
现在处理错误时我会做类似的事情:
processedValueOption match {
case None=> .... // server is not identified
case Some(Failure(e)) => //error to get value from server
case .......
}
实际上我的类型让我知道代码末尾的错误是什么。 但是当你有很大的逻辑时,它就变成了非常复杂的类型。 您认为我应该如何编写代码?我在编写逻辑时是否应该处理错误以获得简单的类型?
答案 0 :(得分:1)
Either
可以成为此类案例的更好解决方案之一,
// Explicit errors
object MyErrors {
trait MyError
object ServerNotIdentifiedError extends MyError
object CanNotGetValueFromServerError extends MyError
object ValueProcessingFailedError extends MyError
}
val server: Either[ Server, MyError ] = serverList.get("serverId").match {
case Some( server ) => Left( server )
case None => Right( ServerNotIdentifiedError )
}
// assuming getDataFromServer returns Try[ Value ]
val value:Either[ Value, MyError ] = getDataFromServer( serverId ) match {
case Success( value ) => Left( value ),
case Failure( ex ) => {
ex.printStackTrace();
Right( CanNotGetValueFromServerError )
}
}
// assuming processValue returns Try[ OtherValue ]
val processedValue: Either[ OtherValue, MyError ] = value match {
case Left( value ) => processValue(value) match {
case Success( otherValue ) => Left( otherValue )
case Failure( ex ) => {
ex.printStackTrace()
Right( ValueProcessingFailedError )
}
}
case _ => value
}
答案 1 :(得分:0)
在Haskell中,通常使用Either
,EitherT
或ExceptT
(EitherT
和ExceptT
是monad变换器并且几乎完全相同)。您可以将do
表示法与其中任何一个一起用于将可能失败的操作串联起来,并获得最终结果或有关失败的信息。在某些情况下,可以使用Applicative
运算符。例如,
f <$> e1 <*> e2 <*> e3
将计算e1
,e2
和e3
,如果失败则停止,如果全部成功,则会将结果与f
合并。您甚至可以将Alternative
加入混合中:
f <$> e1 <*> (e2 <|> e3)