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 | |
} | |
} |
This comment has been minimized.
This comment has been minimized.
def index(id:String) = Action {
(Cache.get(id), Cache.get(id2)) match {
case (Some(_), Some(_)) => Ok
case _ => NotFound
}
} 普通はforを使うと思います |
This comment has been minimized.
This comment has been minimized.
↑だめじゃん... |
This comment has been minimized.
This comment has been minimized.
def index(id:String) = Action {
val maybeId2 = Cache.get(id)
(maybeId2, maybeId2.flatMap(Cache.get)) match {
case (Some(_), Some(result)) => Ok(result)
case (Some(_), None) => NotFound
case (None, None) => NotFound
}
} こうかな |
This comment has been minimized.
This comment has been minimized.
チェーンが増えた時困りそう |
This comment has been minimized.
This comment has been minimized.
複雑になったときの懸念点を明確にするために、もうちょい複雑な例にしてみますね。 def index(id:String) = Action {
Cache.get(id) match {
case Some(id2) => {
Cache.get(id2) match {
case Some(id3) => {
Cache.get(id3) match {
case Some(result) => Ok(result)
case None => NotFound
}
}
case None => BadRequest
}
}
case None => NotFound
}
} これをかっこよくどう書くかですよね。 |
This comment has been minimized.
This comment has been minimized.
うわーw |
This comment has been minimized.
This comment has been minimized.
Either使ってチェーンして最後に判定が良いような気がして来た |
This comment has been minimized.
This comment has been minimized.
def index(id:String) = Action {
def getOrNotFound(id: String): Either[Response, String] = Cache.get(id) match {
case Some(result) => Right(result)
case _ => Left(NotFound)
}
def getOrBadRequest(id: String): Either[Response, String] = Cache.get(id) match {
case Some(result) => Right(result)
case _ => Left(BadRequest)
}
getOrNotFound(id)
.right.flatMap(getOrBadRequest)
.right.flatMap(getOrNotFound) match {
case Right(result) => Ok(result)
case Left(result) => result
}
}
んー |
This comment has been minimized.
This comment has been minimized.
def index(id: String) = Action {
(Cache.getAs[String](id) match {
case None => Left(NotFound)
case Some(id2) => Right(Cache.getAs[String](id2))
}).right.flatMap {
case None => Left(BadRequest)
case Some(id3) => Right(Cache.getAs[String](id3))
}.fold(
r => r,
{
case None => NotFound
case Some(result) => Ok(result)
}
)
} (;´∀`)…うわぁ… |
This comment has been minimized.
This comment has been minimized.
パターンマッチなくしてみた def index(id:String) = Action {
def getOrNotFound(id: String): Either[Response, String] =
Cache.get(id).map(Right.apply).getOrElse(Left(NotFound))
def getOrBadRequest(id: String): Either[Response, String] =
Cache.get(id).map(Right.apply).getOrElse(Left(BadRequest))
getOrNotFound(id)
.right.flatMap(getOrBadRequest)
.right.flatMap(getOrNotFound)
.fold(identity,Ok(_))
} |
This comment has been minimized.
This comment has been minimized.
map と orElse じゃダメなんでしょうか Cache.get(id) map { id2 =>
Cache.get(id2) map { id3 =>
Cache.get(id3) map { result =>
OK(result)
} orElse {
NotFound
}
} orElse {
BadRequest
}
} getOrElse {
NotFound
} ダメなんでしょうか、とか言っておきつつボクはだめで、何故かというとエラー処理が離れてしまっているから。 case class RichOption[+T](o: Option[T]) {
def mapOrElse[R](none: => R)(f: T => R): R = o map f getOrElse r
}
implicit def `Option to RichOption`[T](o: Option[T]) = RichOption(o)
Cache.get(id) mapOrElse(none = NotFound) { id2 =>
Cache.get(id2) mapOrElse(none = BadRequest) { id3 =>
Cache.get(id3) mapOrElse(none = NotFound) { OK(_) }
}
} こんなのかなあ |
This comment has been minimized.
This comment has been minimized.
きっと Scalaz を使ってねこはる先生が・・・ |
This comment has been minimized.
This comment has been minimized.
きっと Scalaz を使ってねこはる先生が・・・ |
This comment has been minimized.
This comment has been minimized.
import com.twitter.util.{Return, Throw, Try}
implicit def optW[T](o: Option[T]) = new {
def toTry[E <: Throwable](t: => E): Try[T] = o map { Return(_) } getOrElse Throw(t)
}
class NotFoundException extends Exception
class BadRequestException extends Exception
def index(id: String) = Action {
((for {
id2 <- Cache.getAs[String](id).toTry(new NotFoundException)
id3 <- Cache.getAs[String](id2).toTry(new BadRequestException)
result <- Cache.getAs[String](id3).toTry(new NotFoundException)
} yield Ok(result)) handle {
case _: NotFoundException => NotFound
case _: BadRequestException => BadRequest
})()
} Try便利だよ! |
This comment has been minimized.
This comment has been minimized.
scalaz は知りませんが haskell なら getCache :: String -> Maybe String
nanika id = do
id2 <- getCacheOr id NotFound
id3 <- getCacheOr id2 BadRequest
result <- getCacheOr id3 NotFound
return result
where
getCacheOr id nothing = fromMaybe (Left nothing) $ Right <$> getCache id こんなのですかねえ。Either a が Monad なのがズルイですね… |
This comment has been minimized.
This comment has been minimized.
|
This comment has been minimized.
This comment has been minimized.
以下 Scala の Either a が Monad じゃないことを愚痴るスレ |
This comment has been minimized.
This comment has been minimized.
とにかくこういうときに、
|
This comment has been minimized.
This comment has been minimized.
|
This comment has been minimized.
This comment has been minimized.
それ Scalaz 使えば(ry
ところで、 |
This comment has been minimized.
This comment has been minimized.
|
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
どうでもいい話なんですが。 関数型言語とかで ADT 自然に使ってる人たちはその辺の理解が最初からあるわけですけど、 便利げなメソッドつかったりするのは後でもいいんじゃないでしょうか。 |
This comment has been minimized.
This comment has been minimized.
あと Option にそもそも List や Either にあるような catamorphism な何か(というか fold ですけど)があれば mapOrElse とかなくても get(id).fold(none = NotFound, some = { id2 => .... }) みたいに書けるし fold いれていい気がしますね |
This comment has been minimized.
This comment has been minimized.
import scalaz._
import Scalaz._
def index(id:String) = Action {
def getOrNotFound(id: String): Either[Response, String]
= Cache.get(id).map(_.right).getOrElse(NotFound.left)
def getOrBadRequest(id: String): Either[Response, String]
= Cache.get(id).map(_.right).getOrElse(BadRequest.left)
(getOrNotFound(id) >>= (getOrBadRequest) >>= (getOrNotFound))
.fold(identity, Ok(_))
} |
This comment has been minimized.
This comment has been minimized.
import scalaz._
import Scalaz._
def index = Action {
Cache.getAs[String](id).toSuccess(NotFound).flatMap(Cache.getAs[String](_).toSuccess(BadRequest)).flatMap(Cache.getAs[String](_).toSuccess(NotFound)).fold(identity, Ok(_))
}
} |
This comment has been minimized.
This comment has been minimized.
一行www |
This comment has been minimized.
This comment has been minimized.
???
歴史の話はあまり知りませんが、実際のところ Either という名前に「あってる」のは scala のほうだと思います。 なんかでも、調べたところ scalaz は scala の Either a を Haskell like なモナドにしてくれるらしいのでそれ使うもしくは自分で定義すれば解決ですね。 |
This comment has been minimized.
This comment has been minimized.
やっぱり流石ねこはる先生 (無駄に横に長くてある意味読みづらい) ワンライナー! |
This comment has been minimized.
This comment has been minimized.
よしだくん、ひどい >< |
This comment has been minimized.
This comment has been minimized.
やはりScalazはさいきょーですね! |
This comment has been minimized.
This comment has been minimized.
すいません多分ウソついたかもしれません。調べたら Scalaz を作り始めたかなり初期の時点(Scala の versionは2.7.2 https://github.com/scalaz/scalaz/blob/8648716fd7a5e7967616c1a0a0301f607f537d2c/src/main/scalaz/EitherW.scala Eitherは存在して、それを拡張するためのものもScalazに存在してますね。 ただ、 標準ライブラリの Either の作者は Scalaz の作者と同じ なので
のではなく、
のかなぁーとかそのあたりが気になる |
This comment has been minimized.
This comment has been minimized.
ボクは後者だと思うんですけどねえ、本人にきくのが早そうです |
This comment has been minimized.
This comment has been minimized.
ボクはこう書くかな~ def index(id: String) = Action {
val id2 = Cache.get(id)
val id3 = id2.flatMap(Cache.get)
val res = id3.flatMap(Cache.get)
(id2, id3, res) match {
case (Some(_), Some(_), Some(result)) => Ok(result)
case (Some(_), None , None) => BadRequest
case _ => NotFound
}
} |
This comment has been minimized.
This comment has been minimized.
わーい最初やろうと思ってたことできたよー https://gist.github.com/2383028 こういうコンテナがあれば implicit def optW[T](o: Option[T]) = new {
def calc[R](r: => R): Container[R, T] = o map { Calculating(_) } getOrElse Result(r)
}
def index(id: String) = Action {
(for {
id2 <- Cache.get(id).calc(NotFound)
id3 <- Cache.get(id2).calc(BadRequest)
result <- Cache.get(id3).calc(NotFound)
} yield Ok(result)).get
} という感じで書けまする。 |
This comment has been minimized.
This comment has been minimized.
お、いいですね。ちょっとnet.liftweb.common.Box思い出しました。 |
This comment has been minimized.
This comment has been minimized.
<|*|>だとだめで結局flatMapになってねこはる先生と同じになって悔しいビクンビクンッ import scalaz._;import Scalaz._
trait OptionWR[T] {
val v:Option[T]
def orResult(r:Result) = v.toSuccess[Result](r)
}
implicit def toOptionWR[T](opt:Option[T]) = new OptionWR[T]{ val v = opt }
def index(id: String) = Action { (for{
id2 <- Cache.get(id).orResult(NotFound)
id3 <- Cache.get(id2).orResult(BadRequest)
result <- Cache.get(id3).orResult(NotFound)
} yield { result }) fold(identity, Ok(_)) } |
This comment has been minimized.
This comment has been minimized.
中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(_))
} |
This comment has been minimized.
This comment has been minimized.
もしかしてこれでよかったんじゃ…… 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)
} |
This comment has been minimized.
This comment has been minimized.
|
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
不勉強で (Right|Left)Projection について全く誤解していました。Projection の名が示すとおり射影なんですね。コレクションでいうところの view のような。 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
} 例によってコンパイルは試してません |
This comment has been minimized.
This comment has been minimized.
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 |
This comment has been minimized.
This comment has been minimized.
ということで、よしださんの質問に対する回答は恐らく後者の ハー全くお恥ずかしい限りです。まあ mapOrElse も気に入ってるんですけどね。 |
This comment has been minimized.
This comment has been minimized.
みなさんの意見を参考にして、書きたかったコードが書けました。 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)
}
} |
This comment has been minimized.
This comment has been minimized.
またつまらない茶々入れになってしまうのですが、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
}
} |
This comment has been minimized.
This comment has been minimized.
たぶん後でunhappyになると思います… |
This comment has been minimized.
This comment has been minimized.
一点だけ気になったので case Some(r) if r.head == 'x' => Right("foo") これは r == "" のときにNoSuchElementExceptionが飛ぶので、 case Some(r) if r.startsWith("x") => Right("foo") にするべきでしょう。 |
This comment has been minimized.
This comment has been minimized.
確かに。ツッコミありがとうございます。 |
This comment has been minimized.
This comment has been minimized.
Action拡張すれば使い易くなりそう。 Right => Continue Left => Break でAlias切るか。 名前がイマイチだけど。良いネーミング無いかな。 |
This comment has been minimized.
This comment has been minimized.
forもscalazも使わずにシンプルに書けた 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)) |
This comment has been minimized.
This comment has been minimized.
これでもいいけど、上の書き方の方が保守しやすいな 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
} |
This comment has been minimized.
This comment has been minimized.
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 |
This comment has been minimized.