-
-
Save rirakkumya/2382341 to your computer and use it in GitHub Desktop.
def index(id:String) = Action { | |
getFirstData(id) | |
} | |
private def getFirstData(id:String) = { | |
Cache.get(id) match { | |
case Some(id2) => getSecondData(id2) | |
case None => NotFound | |
} | |
} | |
private def getSecondData(id2:String) = { | |
Cache.get(id2) match { | |
case Some(result) => Ok(result) | |
case None => NotFound | |
} | |
} |
def index(id:String) = Action { | |
Cache.get(id) match { | |
case Some(id2) => { | |
Cache.get(id2) match { | |
case Some(result) => Ok(result) | |
case None => NotFound | |
} | |
} | |
case None => NotFound | |
} | |
} |
中2心、大切だと思います
import scalaz._
import Scalaz._
implicit val m = Validation.validationMonad[Status]
def index = Action {
(Cache.getAs[String](id) toSuccess NotFound >>=
(Cache.getAs[String](_) toSuccess BadRequest) >>=
(Cache.getAs[String](_) toSuccess NotFound) fail) ||| (Ok(_))
}
もしかしてこれでよかったんじゃ……
def index(id: String) = Action {
(for {
id2 <- Cache.get(id).toRight(NotFound).right
id3 <- Cache.get(id2).toRight(BadRequest).right
result <- Cache.get(id3).toRight(NotFound).right
} yield result).fold(identity, Ok.apply)
}
👍
不勉強で (Right|Left)Projection について全く誤解していました。Projection の名が示すとおり射影なんですね。コレクションでいうところの view のような。
Haskell のより right とか left とかしないといけなくて面倒な代わりに、left のコンテキストに切り替えて計算とかできるので、こっちのほうが汎用性がありますね。
例えば、エラーレスポンスだけに何か適用したい、みたいな時に下のようにかける。
で、そういうことなら恐らく Left と Right で型が一致している時に限り使えるメソッドもあるだろうと思ったら矢張り implicit conversion が提供されていますね
def index(id: String) = Action {
(for {
id2 <- Cache.get(id).toRight(NotFound).right
id3 <- Cache.get(id2).toRight(BadRequest).right
result <- Cache.get(id3).toRight(NotFound).right
} yield OK(result))
.left.map(_.withHeaders(headersForError)).merge
}
例によってコンパイルは試してません
👍
def index(id: String) = Action {
(for {
id2 <- Cache.get(id).toRight(NotFound).right
id3 <- Cache.get(id2).toRight(BadRequest).right
result <- Cache.get(id3).toRight(NotFound).right
} yield Ok(result)).merge
}
さよならidentity
ということで、よしださんの質問に対する回答は恐らく後者の
「最初から意図して Haskell と異なる形で、EitherをScala標準ライブラリに入れた」
でよいはずです。Haskell より汎用的かつ高機能です(swap や merge は Haskell 標準は提供しない)
ハー全くお恥ずかしい限りです。まあ mapOrElse も気に入ってるんですけどね。
みなさんの意見を参考にして、書きたかったコードが書けました。
冗長かもしれませんが、お仕事で使うには改変にも柔軟に対応しないといけないので。
これから使わせて頂きます。
ありがとうございました。scalaコミュニティ最高!
def index(id:String) = Action {
(for {
id2 <- getFirstData(id1).right
result <- getSecondData(id2).right
} yield Ok(result)).merge
}
private def getFirstData(id:String) = {
Cache.get(id) match {
case Some(r) if r.startsWith("x") => Right("foo")
case Some(r) if r == "badcode" => Left(BadRequest)
case Some(r) => Right(r)
case None => Left(NotFound)
}
}
private def getSecondData(id:String) = {
Cache.get(id) match {
case Some(r) => Right(r)
case None => Left(NotFound)
}
}
またつまらない茶々入れになってしまうのですが、get(First|Second)Data みたいなのは、下のように書くとハッピーかもしれません。
def getFirstData(id: String) = {
def validateWith(id: String)(cont: String => String) = for {
r <- get(id).toRight(NotFound).right
r2 <- Either.cond(r != "badrequest", r, BadRequest).right
} yield cont(r2)
validateWith(id) {
case x if x.startsWith("x") => "foo"
case x => x
}
}
たぶん後でunhappyになると思います…
一点だけ気になったので
case Some(r) if r.head == 'x' => Right("foo")
これは r == "" のときにNoSuchElementExceptionが飛ぶので、
case Some(r) if r.startsWith("x") => Right("foo")
にするべきでしょう。
確かに。ツッコミありがとうございます。
Action拡張すれば使い易くなりそう。 Right => Continue Left => Break でAlias切るか。 名前がイマイチだけど。良いネーミング無いかな。
forもscalazも使わずにシンプルに書けた
playで使うならこれが一番良さそう
case class Bind[a1,a2](x:Either[a1,a2]) {
def >>=[b](f:a2 => Either[a1,b]):Either[a1,b] = x.right flatMap f
}
implicit def either2Bind[a](s:Either[Result,a]) = Bind(s)
def index(id:String) = Action {
Right(id) >>= firstData >>= secondData >>= result merge
}
private def firstData(id:String) = {
Cache.get(id) match {
case Some(r) if r.startsWith("x") => Right("foo")
case Some(r) => Right(r)
case None => Left(NotFound)
}
}
private def secondData(id:String) = {
Cache.get(id) match {
case Some(r) if r == "badcode" => Left(BadRequest)
case Some(r) => Right(r)
case None => Left(NotFound)
}
}
private def result(id:String) = Right(Ok(id))
これでもいいけど、上の書き方の方が保守しやすいな
case class Bind[a1,a2](x:Either[a1,a2]) {
def >>=[b](f:a2 => Either[a1,b]):Either[a1,b] = x.right flatMap f
}
implicit def either2Bind[a](s:Either[Result,a]) = Bind(s)
def index(id:String) = Action {
Right(id) >>= { id =>
Cache.get(id) match {
case Some(r) if r.startsWith("x") => Right("foo")
case Some(r) => Right(r)
case None => Left(NotFound)
}
} >>= { id =>
Cache.get(id) match {
case Some(r) if r == "badcode" => Left(BadRequest)
case Some(r) => Right(r)
case None => Left(NotFound)
}
} >>= { r => Right(Ok(r)) } merge
}
https://github.com/scalaz/scalaz/blob/v7.0.0/core/src/main/scala/scalaz/Kleisli.scala#L15
Kleisli の合成だった
trait Response
case object BadRequest extends Response
case object NotFound extends Response
case class Ok(message: String) extends Response
object Cache{ def get(id: String): Option[String] = ??? }
import scalaz._,Scalaz._
val getOrNotFound = Kleisli[({type λ[+α]=Either[Response, α]})#λ, String, String]{
id => Cache.get(id) toRight NotFound
}
val getOrBad = Kleisli[({type λ[+α]=Either[Response, α]})#λ, String, String]{
id => Cache.get(id) toRight BadRequest
}
def index(id: String) = (getOrNotFound >=> getOrBad >=> getOrNotFound) run id map Ok merge
<|*|>だとだめで結局flatMapになってねこはる先生と同じになって悔しいビクンビクンッ