Skip to content

Instantly share code, notes, and snippets.

@nenono
Created March 17, 2015 07:14
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nenono/b53dd7ce41629071a2fa to your computer and use it in GitHub Desktop.
Save nenono/b53dd7ce41629071a2fa to your computer and use it in GitHub Desktop.
Scalaのかんたんなモナド
// もなもなしたアレ関係
package object Monads {
// Option関係
def optionTest = {
println("--optionTest--")
// Option(Maybe)型は標準搭載
val o1 = Some("string!!")
val o2 = None
// パターンマッチ
List(o1, o2).foreach(_ match {
case Some(str) => println(s"some: ${str}")
case None => println("none!")
})
// getOrElse
val o1_ = o1.getOrElse("defaultString")
val o2_ = o2.getOrElse("defaultString")
println(s"result:${o1_} ${o2_}")
// 合成 何れか一つの束縛が失敗した場合は何もしないで終了します
for {
x <- o1
y <- Some(42)
} println(s"result:${x} ${y}")
// for式はHaskellのdo記法、C#のクエリ構文のように使えます。
// 合成結果の値も使えます (実際はSome(x)の部分が何かの関数呼び出しの結果だと思ってください)
val result = for {
x <- Some(1)
y <- Some(2)
z <- Some(3)
} yield (x, y, z)
// result変数はSome((1, 2, 3))になっています
// getでの取り出しは、Noneの場合にエラーになるので非推奨です
println(result.get)
// C#で言うSelect/SelectManyを直接書くならmap, flatMapを使います
val result2 =
Some(1).flatMap(x =>
Some(2).flatMap(y =>
Some(3).map(z =>
(x, y, z))))
println(result2.get)
}
// List
def listTest = {
println("--listTest--")
// Listも当然monadなのでfor記法が使えます (そもそもList内包記法なんだから、と言う意見は無視します)。
// とはいってもn重ループのようなパターンでは素直に使っていきます。
val result = for {
x <- (1 to 5)
y <- (1 to 3)
} yield (x, y)
println(result)
// flatMapで書き下すとたぶんこうなるはずです
val result2 =
(1 to 5).flatMap(x =>
(1 to 3).map(y =>
(x, y)))
println(result2)
// 結果はどちらも以下です
// Vector((1,1), (1,2), (1,3), (2,1), (2,2), (2,3), (3,1), (3,2), (3,3), (4,1), (4,2), (4,3), (5,1), (5,2), (5,3))
}
// Either
def eitherTest = {
println("--eitherTest--")
// 失敗時の値も保持できるOptionのようなものです
// 参考: http://yuroyoro.hatenablog.com/entry/20100719/1279519961
val success = Right(42)
val error = Left("some error occurred!")
// パターンマッチ
List(success, error).foreach(_ match {
case Right(x) => println(s"success! value:${x}")
case Left(y) => println(s"failure! value:${y}")
})
// この辺り結構型推論頑張ってくれてるっぽい感じがしますね
// for式も使えます (実際はRight(x)/Left(x)の部分が何かの関数呼び出しの結果だと思ってください)
val result = for {
a <- Right(1).right
b <- Right(2).right
c <- Left("some error occurred!").right
d <- Right(3).right
} yield (a, b, c, d)
println(s"result:${result}") // Left("some error occurred!") になります
// flatMapはEither[A, B]のright/leftに定義されていて、
// right/leftで指定した方を「成功」とみなして次のステップに進むようになっているようです
// ちょっと面倒くさいところですね
// ガチで使うならscalazのEitherやValidationを使うほうが良いみたいです……
// 機械的にflatMapで書き下すとこんな感じです
val result2 =
Right(1).right.flatMap(a =>
Right(2).right.flatMap(b =>
Left("some error occurred!").right.flatMap(c =>
Right(3).right.map(d =>
(a, b, c, d)))))
println(s"result:${result2}")
}
def main(args: Array[String]) {
optionTest
listTest
eitherTest
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment