scala:提高这段代码的可读性和风格
下面是一个非常常见的play framework 2控制器:scala:提高这段代码的可读性和风格,scala,error-handling,idioms,Scala,Error Handling,Idioms,下面是一个非常常见的play framework 2控制器: def save(ideaId : Long) = CORSAction { request => Idea.findById(ideaId).map { idea => request.body.asJson.map { json => json.asOpt[Comment].map { comment => comment.copy(idea = idea).save
def save(ideaId : Long) = CORSAction { request =>
Idea.findById(ideaId).map { idea =>
request.body.asJson.map { json =>
json.asOpt[Comment].map { comment =>
comment.copy(idea = idea).save.fold(
errors => JsonBadRequest(errors),
comment => Ok(toJson(comment).toString)
)
}.getOrElse (JsonBadRequest("Invalid Comment entity"))
}.getOrElse (JsonBadRequest("Expecting JSON data"))
}.getOrElse (JsonBadRequest("Could not find idea with id '%s'".format(ideaId)))
}
我发现所有嵌套的.maps都有点烦人,而且我还发现每个错误处理都在底部有点乏味
您将如何改进它以使其更具可读性,同时保持其作为功能惯用的scala代码
我在想可能是这样的(这是seudo代码,仍然没有编译)
ps:我知道最好避免使用return语句…先简化一下。假设我有三个方法,它们接受
字符串
并返回选项[String]
:
def foo(s: String): Option[String] = if (s.size >= 4) Some(s + "1") else None
def bar(s: String): Option[String] = if (s(0) != 'A') Some(s + "2") else None
def baz(s: String): Option[String] = if (s toSet ' ') Some(s + "3") else None
我想要一个方法,该方法通过管道传输字符串,并在过程中得到None
时返回相应的错误消息。我可以这样写:
def all(s: String): Either[String, String] =
foo(s).map { x =>
bar(x).map { y =>
baz(y).map { z =>
Right(z)
} getOrElse Left("Doesn't contain a space!")
} getOrElse Left("Starts with an A!")
} getOrElse Left("Too short!")
但对了,这并不漂亮。我们可以使用进行理解,使用选项上的toRight
方法编写更清晰的版本:
def all(s: String): Either[String, String] = for {
x <- (foo(s) toRight "Too short!" ).right
y <- (bar(x) toRight "Starts with an A!" ).right
z <- (baz(y) toRight "Doesn't contain a space!").right
} yield z
虽然没有您想要的语法那么简洁,但错误消息显示在更符合逻辑的地方,我们已经摆脱了难看的嵌套。祝贺您,您刚刚发明了monad!卸下返回
s作为起动机。哦,等等,他们应该破坏save
方法的流程吗?我认为嵌套贴图实际上是平面贴图,否则你会得到[other[…],other[…],不是吗?@pedrofurla:在嵌套版本中,getOrElse
在每一个级别都已经在进行平面化了。@om non nom:谢谢编辑!我发誓我从来没有用Scala打过这个词!
def all(s: String): Either[String, String] = for {
x <- (foo(s) toRight "Too short!" ).right
y <- (bar(x) toRight "Starts with an A!" ).right
z <- (baz(y) toRight "Doesn't contain a space!").right
} yield z
def save(ideaId: Long) = CORSAction { request =>
val saveResult = for {
idea <- (Idea.findById(ideaId) toRight "Could not find id" ).right
json <- (request.body.asJson toRight "Invalid Comment entity").right
comment <- (json.asOpt[Comment] toRight "Expecting JSON data" ).right
result <- comment.copy(idea = idea).save().right
} yield result
saveResult.fold(
error => JsonBadRequest(error),
comment => Ok(toJson(comment).toString)
)
}