Skip to content

Instantly share code, notes, and snippets.

@rirakkumya
Created April 14, 2012 05:55
Show Gist options
  • Save rirakkumya/2382341 to your computer and use it in GitHub Desktop.
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
}
}
@yuroyoro
Copy link

<|*|>だとだめで結局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(_)) }

@halcat0x15a
Copy link

中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(_))
}

@gakuzzzz
Copy link

もしかしてこれでよかったんじゃ……

  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)
  }

@tototoshi
Copy link

👍

@tototoshi
Copy link

http://togetter.com/li/287993
貼っておこう

@lyricallogical
Copy link

不勉強で (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
  }

例によってコンパイルは試してません

@kxbmap
Copy link

kxbmap commented Apr 14, 2012

👍

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

@lyricallogical
Copy link

ということで、よしださんの質問に対する回答は恐らく後者の
「最初から意図して Haskell と異なる形で、EitherをScala標準ライブラリに入れた」
でよいはずです。Haskell より汎用的かつ高機能です(swap や merge は Haskell 標準は提供しない)

ハー全くお恥ずかしい限りです。まあ mapOrElse も気に入ってるんですけどね。

@rirakkumya
Copy link
Author

みなさんの意見を参考にして、書きたかったコードが書けました。
冗長かもしれませんが、お仕事で使うには改変にも柔軟に対応しないといけないので。
これから使わせて頂きます。
ありがとうございました。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)
  }
}

@lyricallogical
Copy link

またつまらない茶々入れになってしまうのですが、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
  }
}

@rirakkumya
Copy link
Author

たぶん後でunhappyになると思います…

@tototoshi
Copy link

一点だけ気になったので

    case Some(r) if r.head == 'x' => Right("foo")

これは r == "" のときにNoSuchElementExceptionが飛ぶので、

    case Some(r) if r.startsWith("x") => Right("foo")

にするべきでしょう。

@rirakkumya
Copy link
Author

確かに。ツッコミありがとうございます。

@rirakkumya
Copy link
Author

Action拡張すれば使い易くなりそう。 Right => Continue Left => Break でAlias切るか。 名前がイマイチだけど。良いネーミング無いかな。

@rirakkumya
Copy link
Author

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))

@rirakkumya
Copy link
Author

これでもいいけど、上の書き方の方が保守しやすいな

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
}

@xuwei-k
Copy link

xuwei-k commented Jun 6, 2013

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment