トップ «前の日記(2014-06-18) 最新 次の日記(2014-07-08)» 編集

Route 477



2014-06-20

[haskell][scala] ScalaのEitherとHaskellのEitherはちょっと違うらしい

このへん見てたら、ScalaのEitherはHaskellのとちょっと違って、より柔軟なことができる(代わりに記述が少しだけ増える)らしいという話があって面白かった。 (※自分用のメモなので分かりにくいかもしれないです。)

お題

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

これをどうやって綺麗に書くか、という話。

でHaskellならEither aがモナドだからEitherとdoでこう書けるんだけどという話が出た

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

で、ScalaのEitherではできないのかというと、実は.rightというメソッドがあるんですね。

RightProjection

HaskellのEitherは「Leftはエラー情報を持ち、Rightは成功時のデータを持つ」という使い方を想定してるので、do記法を使った場合は「Rightなら計算を続け、Leftならそこで中止する」という定義になっている。 一方ScalaのEitherは、LeftもRightも等価なものとして扱えるようになっているみたい。そのため生のEitherはforに使えない。

その代わりRightProjection, LeftProjectionをとるとforで使えるようになる。

a :: Either[String, Int]という変数があったとき、a.rightとすると何が返ってくるか。最初はOption[Int]が返るのかな?と思ったのだけど、RightProjectionのインスタンスが返るらしい。これはもとのEitherをラップしたものだけど、flatMapが定義してあって、「Rightなら計算を続け、Leftならそこで中止する」という定義になっている。で、同様にa.leftとやるとLeftProjectionが返って、こっちのflatMapは「Leftなら計算を続け、Rightならそこで中止する」という定義になっていると。

ちなみに

scalazではEither自体もflatMapできるらしい。 http://togetter.com/li/287993?page=5 http://d.hatena.ne.jp/cooldaemon/20111017/1318862426