-
-
Save aoiroaoino/f017458c29d0b98eeaf239529f04af22 to your computer and use it in GitHub Desktop.
Scala秋祭り2019 発表資料内に登場するソースコードの元
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| def isEven(i: Int): Boolean = 1 % 2 == 0 | |
| val result = isEven(42) | |
| if (result) { | |
| println(s"$n is even number") | |
| } else { | |
| println(s"$n is odd number") | |
| } | |
| // === | |
| val result = isEven(42) | |
| if (result) { | |
| println(s"$n is even number") // 実行される | |
| } else { | |
| println(s"$n is odd number") // 実行されない(捨てられる) | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // === | |
| def add(i: Int, j: Int): Int = i + j | |
| def mul(i: Int, j: Int): Int = i * j | |
| def show(i: Int): String = "num: " + i.toString | |
| val a = add(1, 2) | |
| val b = add(a, 3) | |
| val s = show(b) | |
| println(s) | |
| // === | |
| def add(i: Int, j: Int): Int = i + j | |
| def mul(i: Int, j: Int): Int = i * j | |
| def show(i: Int): String = "num: " + i.toString | |
| val a = add(1, 2) // 計算結果 | |
| // ↓ その後の計算 | |
| val b = mul(a, 3) | |
| val s = show(b) | |
| println(s) | |
| // === | |
| def add(i: Int, j: Int): Int = i + j | |
| def mul(i: Int, j: Int): Int = i * j | |
| def show(i: Int): String = "num: " + i.toString | |
| val a = add(1, 2) | |
| val b = mul(a, 3) // 計算結果 | |
| // その後の計算 | |
| val s = show(b) | |
| println(s) | |
| // === | |
| def add(i: Int, j: Int): Int = i + j | |
| def mul(i: Int, j: Int): Int = i * j | |
| def show(i: Int): String = "num: " + i.toString | |
| val a = add(1, 2) | |
| val b = mul(a, 3) | |
| val s = show(b) // 計算結果 | |
| // その後の計算 | |
| println(s) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 通常の計算(直接スタイル) | |
| def add(i: Int, j: Int): Int = i + j | |
| // 継続渡しスタイル | |
| def add[R](i: Int, j: Int)(cont: Int => R): R = { | |
| val a = i + j | |
| cont(a) | |
| } | |
| // 通常の計算(直接スタイル) | |
| def show(i: Int): String = "num: " + i.toString | |
| // 継続渡しスタイル | |
| def show[R](i: Int)(cont: String => R): R = { | |
| val a = "num: " + i.toString | |
| cont(a) | |
| } | |
| // === | |
| scala> def add[R](i: Int, j: Int)(cont: Int => R): R = { | |
| | val a = i + j | |
| | cont(a) | |
| | } | |
| add: [R](i: Int, j: Int)(cont: Int => R)R | |
| // 1 + 2 を実行。その後の計算で「10倍する」 | |
| scala> add(1, 2)(a => a * 10) | |
| res0: Int = 30 | |
| // 1 + 2 を実行。その後の計算で「引数を返す」 | |
| scala> add(1, 2)(a => a) | |
| res1: Int = 3 | |
| // === | |
| scala> def show[R](i: Int)(cont: String => R): R = { | |
| | val a = "num: " + i.toString | |
| | cont(a) | |
| | } | |
| show: [R](i: Int)(cont: String => R)R | |
| // 1 を文字列に変換し prefix をつける。その後の計算「suffix をつける」 | |
| scala> show(1)(a => a + " !!") | |
| res3: String = num: 1 !! | |
| // 1 を文字列に変換し prefix をつける。その後の計算「Byte 配列を得る」 | |
| scala> show(1)(a => a.getBytes(java.nio.charset.StandardCharsets.UTF_8)) | |
| res4: Array[Byte] = Array(110, 117, 109, 58, 32, 49) | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // === | |
| // 直接スタイル(再掲) | |
| val a = add(1, 2) | |
| val b = mul(a, 3) | |
| val s = show(b) | |
| println(s) | |
| // === | |
| // 継続渡しスタイル | |
| add(1, 2){ a => | |
| mul(a, 3){ b => | |
| show(b){ s => | |
| println(s) | |
| } | |
| } | |
| } | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // === | |
| // 継続ってどこだっけ? | |
| def add[R](i: Int, j: Int)(cont: Int => R): R = { | |
| val a = i + j | |
| cont(a) | |
| } | |
| def show[R](i: Int)(cont: String => R): R = { | |
| val a = "num: " + i.toString | |
| cont(a) | |
| } | |
| // === | |
| // メソッド定義を再掲 | |
| def add[R](i: Int, j: Int)(cont: Int => R): R = { ??? } | |
| def show[R](i: Int) (cont: String => R): R = { ??? } | |
| // 継続部分を関数に変更 | |
| def add[R](i: Int, j: Int): (Int => R) => R = { cont => ??? } | |
| def show[R](i: Int) : (String => R) => R = { cont => ??? } | |
| // (A => R) => R という関数に Cont[R, A] と名前をつける | |
| type Cont[R, A] = (A => R) => R | |
| def add[R](i: Int, j: Int): Cont[R, Int] = { cont => ??? } | |
| def show[R](i: Int) : Cont[R, String] = { cont => ??? } | |
| // === | |
| // Scala ではしばしばラップするデータ型を定義 | |
| final case class Cont[R, A](run: (A => R) => R) | |
| // もちろん、データ型 Cont[R, A] を用いて add, show が定義できる | |
| def add[R](i: Int, j: Int): Cont[R, Int] = Cont { cont => ??? } | |
| def show[R](i: Int) : Cont[R, String] = Cont { cont => ??? } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // === | |
| // Monad にしたい(for式で合成したい) | |
| object Cont { | |
| def pure[R, A](a: A): Cont[R, A] = | |
| ??? | |
| } | |
| final case class Cont[R, A](run: (A => R) => R) { | |
| def flatMap[B](f: A => Cont[R, B]): Cont[R, B] = | |
| ??? | |
| def map[B](f: A => B): Cont[R, B] = | |
| ??? | |
| } | |
| // === | |
| // pure の実装 | |
| object Cont { | |
| def pure[R, A](a: A): Cont[R, A] = | |
| Cont(ar => ar(a)) // pure の実装 | |
| } | |
| final case class Cont[R, A](run: (A => R) => R) { | |
| def flatMap[B](f: A => Cont[R, B]): Cont[R, B] = | |
| ??? | |
| def map[B](f: A => B): Cont[R, B] = | |
| ??? | |
| } | |
| // === | |
| // flatMap の実装 1 | |
| object Cont { | |
| def pure[R, A](a: A): Cont[R, A] = | |
| Cont(ar => ar(a)) | |
| } | |
| final case class Cont[R, A](run: (A => R) => R) { | |
| def flatMap[B](f: A => Cont[R, B]): Cont[R, B] = | |
| Cont(br => ???) // 計算結果を考える | |
| def map[B](f: A => B): Cont[R, B] = | |
| ??? | |
| } | |
| // === | |
| // flatMap の実装 2 | |
| object Cont { | |
| def pure[R, A](a: A): Cont[R, A] = | |
| Cont(ar => ar(a)) | |
| } | |
| final case class Cont[R, A](run: (A => R) => R) { | |
| def flatMap[B](f: A => Cont[R, B]): Cont[R, B] = | |
| Cont(br => run(a => f(a) /* Cont[R, B] */)) // 自身の継続として、引数の関数fにその結果を与える | |
| def map[B](f: A => B): Cont[R, B] = | |
| ??? | |
| } | |
| // === | |
| // flatMap の実装 3 | |
| object Cont { | |
| def pure[R, A](a: A): Cont[R, A] = | |
| Cont(ar => ar(a)) | |
| } | |
| final case class Cont[R, A](run: (A => R) => R) { | |
| def flatMap[B](f: A => Cont[R, B]): Cont[R, B] = | |
| Cont(br => run(a => f(a).run(br))) // fに結果を与えて得られた継続に外側の継続を渡す | |
| def map[B](f: A => B): Cont[R, B] = | |
| ??? | |
| } | |
| // === | |
| // map はモナドのデフォルト実装 | |
| object Cont { | |
| def pure[R, A](a: A): Cont[R, A] = | |
| Cont(ar => ar(a)) | |
| } | |
| final case class Cont[R, A](run: (A => R) => R) { | |
| def flatMap[B](f: A => Cont[R, B]): Cont[R, B] = | |
| Cont(br => run(a => f(a).run(br))) | |
| def map[B](f: A => B): Cont[R, B] = | |
| flatMap(a => Cont.pure(f(a))) // ひとまずモナドのデフォルト実装 | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // === | |
| sbt.version=1.2.8 | |
| ThisBuild / scalaVersion := "2.13.0" | |
| ThisBuild / version := "0.1.0-SNAPSHOT" | |
| ThisBuild / organization := "com.example" | |
| ThisBuild / organizationName := "example" | |
| lazy val root = (project in file(".")) | |
| .settings( | |
| name := "check_cont_monad_law", | |
| libraryDependencies += "org.scalacheck" %% "scalacheck" % "1.14.0" % Test | |
| ) | |
| // === | |
| package example | |
| import org.scalacheck.{Prop, Properties} | |
| final case class Cont[R, A](run: (A => R) => R) { | |
| def map[B](f: A => B): Cont[R, B] = Cont(br => run(f andThen br)) | |
| def flatMap[B](f: A => Cont[R, B]): Cont[R, B] = | |
| Cont(br => run(f(_).run(br))) | |
| } | |
| object Cont { | |
| def pure[R, A](a: A): Cont[R, A] = Cont(_(a)) | |
| } | |
| class ContMonadSpec extends Properties("Monad[Cont[R, ?]]") { | |
| def inc(i: Int): Cont[Int, Int] = Cont(_(i + 1)) | |
| def add_: Cont[String, String] = Cont(_(s + "!")) | |
| def add_?[R](s: String): Cont[String, String] = Cont(_(s + "?")) | |
| property("rightIdentity") = Prop.forAll { i: Int => | |
| inc(i).flatMap(Cont.pure).run(identity) == inc(i).run(identity) | |
| } | |
| property("leftIdentity") = Prop.forAll { i: Int => | |
| Cont.pure[Int, Int](i).flatMap(inc).run(identity) == inc(i).run(identity) | |
| } | |
| property("associativity") = Prop.forAll { s: String => | |
| Cont.pure(s).flatMap(add_!).flatMap(add_?).run(identity) == | |
| Cont.pure(s).flatMap(a => add_!(a).flatMap(add_?)).run(identity) | |
| } | |
| } | |
| // === | |
| sbt:check_cont_monad_law> test | |
| [info] + Monad[Cont[R, ?]].rightIdentity: OK, passed 100 tests. | |
| [info] + Monad[Cont[R, ?]].leftIdentity: OK, passed 100 tests. | |
| [info] + Monad[Cont[R, ?]].associativity: OK, passed 100 tests. | |
| [info] Passed: Total 3, Failed 0, Errors 0, Passed 3 | |
| [success] Total time: 1 s, completed 2019/09/15 19:49:36 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // === | |
| def add[R](i: Int, j: Int): Cont[R, Int] = Cont(ar => ar(i + j)) | |
| def mul[R](i: Int, j: Int): Cont[R, Int] = Cont(ar => ar(i * j)) | |
| def show[R](i: Int): Cont[R, String] = Cont(ar => ar(s"num: $i")) | |
| def prog[R]: Cont[R, String] = | |
| for { | |
| a <- add(1, 2) | |
| b <- mul(a, 3) | |
| s <- show(b) | |
| } yield { | |
| s.toUpperCase | |
| } | |
| // === | |
| scala> prog.run(s => s.toList) | |
| res18: List[Char] = List(N, U, M, :, , 9) | |
| scala> prog.run(s => s.length) | |
| res19: Int = 6 | |
| scala> prog.run(s => s) | |
| res20: String = NUM: 9 | |
| // === | |
| def prog[R]: Cont[R, String] = | |
| add(1, 2).flatMap { a => | |
| mul(a, 3).flatMap { b => | |
| show(b).map { s => | |
| s.toUpperCase | |
| } | |
| } | |
| } | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // fizzbuzz | |
| def fizzCont(i: Int): Cont[String, Int] = Cont { ar => | |
| if (i % 3 == 0) { | |
| "Fizz" // 継続(ar)を実行しないので計算が "Fizz" で終了する | |
| } else { | |
| ar(i) // 継続に i を渡し、残りの処理を実行する | |
| } | |
| } | |
| // === | |
| def fizzCont(i: Int): Cont[String, Int] = Cont(ar => if (i % 3 == 0) "Fizz" else ar(i)) | |
| def buzzCont(i: Int): Cont[String, Int] = Cont(ar => if (i % 5 == 0) "Buzz" else ar(i)) | |
| def fizzBuzzCont(i: Int): Cont[String, Int] = Cont(ar => if (i % 15 == 0) "FizzBuzz" else ar(i)) | |
| def fizzBuzz(i: Int): Cont[String, Int] = | |
| for { | |
| a <- fizzBuzzCont(i) | |
| b <- fizzCont(a) | |
| c <- buzzCont(b) | |
| } yield c | |
| // === | |
| scala> LazyList.from(1).map(fizzBuzz(_).run(_.toString)).take(15).toList | |
| res54: List[String] = List(1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz) | |
| // === | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import scala.util.Try | |
| // Some だったら継続に値を渡して実行。None だったら継続を破棄して ifNone の結果を返す | |
| def someValueOr[R, A](fa: Option[A])(ifNone: => R): Cont[R, A] = | |
| Cont(ar => fa.fold(ifNone)(ar)) // Cont(fa.fold(ifNone)) | |
| // Success だったら継続に値を渡して実行。None だったら継続を破棄して ifFailure の結果を返す | |
| def successValueOr[R, A](fa: Try[A])(ifFailure: Throwable => R): Cont[R, A] = | |
| Cont(ar => fa.fold(ifFailure, ar)) // Cont(fa.fold(ifFailure, _)) | |
| // input から指定された key に対応する値を取り出し、数値型に変換する | |
| def parseInt(input: Map[String, String], key: String): Cont[String, Int] = | |
| for { | |
| s <- someValueOr(input.get(key))(s"not found: $key") | |
| i <- successValueOr(Try(s.toInt))(_.toString) | |
| } yield i | |
| // === | |
| // 例えば下記のような input を仮定する | |
| val input = Map("name" -> "aoiroaoino", "age" -> "17") | |
| parseInt(input, "address").run(i => s"result: $i") | |
| parseInt(input, "name").run(i => s"result: $i") | |
| parseInt(input, "age").run(i => s"result: $i") | |
| // === | |
| scala> parseInt(input, "address").run(i => s"result: $i") | |
| res99: String = not found: address | |
| scala> parseInt(input, "name").run(i => s"result: $i") | |
| res100: String = java.lang.NumberFormatException: For input string: "aoiroaoino" | |
| scala> parseInt(input, "age").run(i => s"result: $i") | |
| res101: String = result: 17 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // === | |
| // Scala 2.13.0 より Using が入った | |
| def resource[R, A](a: => A)(implicit F: Releasable[A]): Cont[R, A] = | |
| Cont(ar => Using.resource(a)(ar)) | |
| // === |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // === | |
| type User = String | |
| type Result = String | |
| def findAll[R](): Cont[Result, List[User]] = Cont { ar => | |
| try { | |
| ar(List("foo", "bar")) // DB への問い合わせは成功したとする | |
| } catch { | |
| case _: Throwable => "query execution error" | |
| } | |
| } | |
| // === | |
| val prog: Cont[Result, List[User]] = | |
| for { | |
| users <- findAll() | |
| names <- Cont.pure(users.map(n => s"[$n]")) | |
| } yield names | |
| scala> prog.run(_.mkString(", ")) | |
| res132: Result = [foo], [bar] | |
| // === | |
| val prog: Cont[Result, List[User]] = | |
| for { | |
| users <- findAll() | |
| names <- Cont.pure(users.map(n => s"[$n]")) | |
| _ = 1 / 0 | |
| } yield names | |
| scala> prog.run(_.mkString(", ")) | |
| res133: Result = query execution error |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // M[_] はモナドの型 | |
| def pure[A](a: A): M[A] | |
| def flatMap[B](f: A => M[B]): M[B] | |
| // 右単位元 | |
| fa.flatMap(a => pure(a)) == fa | |
| // 左単位元 | |
| pure(a).flatMap(f) == f(a) | |
| // 結合律 | |
| fa.flatMap(f).flatMap(g) == fa.flatMap(a => f(a).flatMap(g)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment