SlideShare uma empresa Scribd logo
1 de 18
Baixar para ler offline
Scalaによる型安全な 
エラーハンドリング 
安田裕介
型システムを崩壊させるもの 
• 例外 
• 部分関数:一部の入力が関数のドメインに含まれて 
いない関数
例外の危険性 
• 参照透明ではない 
• 型安全ではない 
def willThrow[A,B](a: A): B = throw new Exception 
• 高階関数との相性が悪い 
def highOrder[A,B](f: A => B) = { 
//fが例外を投げるとしたらどう対処するのか?? 
} 
⇒どのtry式に属するかで結果が変わる 
↓この関数の型のどこに例外の情報が?
関数型プログラミングでは 
例外の代わりに 
失敗を表現する型と 
その値を用いる 
※Scalaはオブジェクト指向言語でもあるので、例外は存在する
Option型 
成功したときの値をもつSomeと、 
失敗を表すNoneからなる型 
sealed abstract class Option[+A] 
! 
case class Some[+A](x: A) extends Option[A] 
! 
case object None extends Option[Nothing]
1 sealed abstract class Option[+A] { 
2 //値があるか判定 
3 def isEmpty: Boolean 
4 //値を取得 
5 def get: A 
6 //値を取得し、取得できなかった場合はデフォルト値を使用 
7 final def getOrElse[B >: A](default: => B): B 
8 //値に対してfを適用したOptionを返す 
9 final def map[B](f: A => B): Option[B] 
10 //値に対して失敗する可能性のあるfを適用したOptionを返す 
11 final def flatMap[B](f: A => Option[B]): Option[B] 
12 //述語関数を満たす値のOptionを取得 
13 final def filter(p: A => Boolean): Option[A] 
14 //値がなければデフォルトのOptionを返す 
15 final def orElse[B >: A](alternative: => Option[B]): Option[B] 
16 } 
17 
18 final case class Some[+A](x: A) extends Option[A] { 
19 def isEmpty = false 
20 def get = x 
21 } 
22 
23 case object None extends Option[Nothing] { 
24 def isEmpty = true 
25 def get = throw new NoSuchElementException("None.get") 
26 }
Optionを使った関数 
Listの先頭要素を返す関数headは 
空のList()で未定義な部分関数だ 
1 def head[T](xs: List[T]) = xs.head 
2 
3 scala> head(List()) 
4 // java.util.NoSuchElementException: head of empty list 
5 // at scala.collection.immutable.Nil$.head(List.scala:420) 
6 // at scala.collection.immutable.Nil$.head(List.scala:417) 
7 // at .head(<console>:7) 
8 // ... 33 elided 
9 
10 def head[T](xs: List[T]) 
= if(xs.isEmpty) None else Some(xs.head) 
11 
12 scala> head(List()) 
13 // res1: Option[Nothing] = None 
14 
15 scala> head(List(1,2,3)) 
16 res2: Option[Int] = Some(1) 
←コンパイルは通る 
←要素のないリストでは実行時エラー 
←Optionを使う 
←失敗した場合はNoneが返る 
←成功した場合はSomeが返る
Optionから値を取り出す 
1 scala> val odd = head(List(1,2,3)) 
.filter(_ % 2 == 1).map(_*3).getOrElse(1) 
2 //odd: Int = 3 
3 scala> val even = head(List(1,2,3)) 
.filter(_ % 2 == 0).map(_*2).getOrElse(0) 
4 //even: Int = 0 
5 scala> val none = head(List[Int]()) 
.filter(_ % 2 == 0).map(_*2).getOrElse(0) 
6 //none: Int = 0 
7 
8 val matched = head(List(1,2,3)) match { 
9 case Some(x) => x 
10 case None => 0 
11} 
12 matched: Int = 1 
いずれもエラーにはならない
Optionは部分関数を 
全関数にする 
def head[T](xs: List[T]): Int 
List[Int] Int 
List() 
def head[T](xs: List[T]): Option[Int] 
List[Int] Option[Int] 
List() 
関数型プログラミングでは全関数になるように設計する
PartialFunction型 
部分関数を表すPartialFunction型を作れる 
←パターンマッチによる 
先頭要素取り出し 
ケースシーケンスを使えば 
1 val head: PartialFunction[List[Int], Int] = { 
2 case x::xs => x 
3 } 
4 
3 val head2: Function1[List[Int], Int] = { 
4 case x::xs => x 
5 } 
6 //<console>:12: warning: match may not be exhaustive. 
7 //It would fail on the following input: Nil 
5 
6 head(List(1,2,3)) 
7 //res1: Int = 1 
9 
10 head.isDefinedAt(List()) 
11 //res2: Boolean = false 
13 
14 val optioned = head.lift 
15 optioned(List()) 
16 //res3: Option[Int] = None 
PartialFunction型以外では 
部分関数を渡すと警告 
←入力が関数のドメインに 
含まれるか判定 
←Optionを返す関数に変換
Optionの合成 
複数の値が失敗する可能性がある場合どうする? 
def optionPlus(a: Option[Int], b: Option[Int]): Int = ??? 
例)2つの数字文字列を合計する 
1 def stringToInt(s: String) = try { 
2 Some(s.toInt) 
3 } catch { case e: Exception => None } 
8 
11 def map2[A,B,C](oa: Option[A], ob: Option[B])(f: (A,B) => C) 
= oa.flatMap(a => ob.map(b => f(a,b))) 
12 
13 val optionPlus = map2(_: Option[Int], _: Option[Int])(_ + _) 
14 
22 val one = stringToInt(“1") //Some(1) 
23 val two = stringToInt(“2") //Some(2) 
24 val fail = stringToInt(“hello") //None 
25 
26 /*scala>*/ optionPlus(one,two) 
mapとflatMapを 
27 //res1: Option[Int] = Some(3) 
29 /*scala>*/ optionPlus(one,fail) 
使えばできる 
30 //res2: Option[Int] = None
for内包表記による合成 
1 val one = stringToInt("1") 
2 val two = stringToInt("2") 
3 val fail = stringToInt("hello") 
4 
5 for { 
6 o <- one 
7 t <- two 
8 } yield o + t 
9 //res1: Option[Int] = Some(3) 
10 
11 for { 
12 o <- one 
13 f <- fail 
14 } yield o + f 
15 //res2: Option[Int] = None 
16 
17 for { 
18 o <- one 
19 t <- two 
20 th <- optionPlus(one,two) 
21 } yield o + t + th 
22 //res3: Option[Int] = Some(6) 
左の式は右と等価 
one.flatMap(o => 
two.map(t => o + t)) 
//res1: Option[Int] = Some(3) 
! 
! 
! 
one.flatMap(o => 
fail.map(f => o + f)) 
//res2: Option[Int] = None 
! 
! 
! 
one.flatMap(o => 
two.flatMap(t => 
optionPlus(one,two).map(th => 
o + t + th))) 
//res3: Option[Int] = Some(6)
Optionの仲間 
• Either[+A, +B]:成功の情報をRightで、失敗の情 
報をLeftで表現する 
• Try[+T]:例外を投げる可能性のある処理を抽象化 
し、SuccessとFailureによってその結果を表現す 
る
おまけ)非同期処理 
Optionで見たmapやforによる合成が普遍的 
に行える例としてFutureを見ていこう 
Future[+T]: 非同期処理を扱い、未来の値を表現する 
1 import scala.concurrent._ 
2 import ExecutionContext.Implicits.global 
3 import scala.util.{Success, Failure} 
4 
5 val f: Future[List[Int]] = Future { 
6 Thread.sleep(5000) 
7 List(1,2,3,4) 
8 } 
9 
Try[+T] 
10 f onComplete { 
11 case Success(list) => list.foreach(println) 
12 case Failure(e) => println(e.getMessage) 
13 } 
14 //1 2 3 4
ネスト 
5 val f: Future[List[Int]] = Future { 
6 Thread.sleep(5000) 
7 List(1,2,3,4) 
8 } 
14 
15 f onComplete { 
16 case Success(list) => { 
17 val f2 = Future { 
18 Thread.sleep(5000) 
19 List(1,2,3,4) 
20 } 
21 f2 onComplete { 
22 case Success(list2) => 
for (l1 <- list; l2 <- list2) println(l1 * l2) 
23 case Failure(e) => println(e.getMessage) 
24 } 
25 } 
26 case Failure(e) => println(e.getMessage) 
27 } 
28 //1 2 3 4 2 4 6 8 3 6 9 12 4 8 12 16
ネストの回避(map) 
def map[S](f: (T) => S)(implicit executor: ExecutionContext) 
: Future[S] 
5 val f: Future[List[Int]] = Future { 
6 Thread.sleep(5000) 
7 List(1,2,3,4) 
8 } 
9 
31 val f2: Future[List[Int]] = f.map(list => { 
32 Thread.sleep(5000) 
33 val list2 = List(1,2,3,4) 
34 for (l1 <- list; l2 <- list2) yield l1 * l2 
35 }) 
36 
37 f2 onSuccess { 
38 case res => res.foreach(println) 
39 } 
28 //1 2 3 4 2 4 6 8 3 6 9 12 4 8 12 16
ネストの回避(for) 
41 val f1: Future[List[Int]] = Future { 
42 Thread.sleep(5000) 
43 List(1,2,3,4) 
44 } 
45 
46 val f2: Future[List[Int]] = Future { 
47 Thread.sleep(5000) 
48 List(1,2,3,4) 
49 } 
50 
51 val f = for { 
52 res1 <- f1 
53 res2 <- f2 
54 } yield for (l1 <- res1; l2 <- res2) yield l1 * l2 
55 
56 f onSuccess { 
57 case res => res.foreach(println) 
58 } 
//1 2 3 4 2 4 6 8 3 6 9 12 4 8 12 16
まとめ 
• 例外と部分関数は型システムをすり抜け、実行時に 
問題を起こす 
• Scalaでは例外ではなくOption、Either、Tryのよ 
うな値を使う 
• Optionによって部分関数を全関数にできる 
• Scalaのfor内包表記はすべてのモナドを扱える強力 
な記法である

Mais conteúdo relacionado

Mais procurados

Lambda in template_final
Lambda in template_finalLambda in template_final
Lambda in template_final
Cryolite
 

Mais procurados (20)

3D で遊ぼう ~C#er も TypeScript で楽々 WebGL~
3D で遊ぼう ~C#er も TypeScript で楽々 WebGL~3D で遊ぼう ~C#er も TypeScript で楽々 WebGL~
3D で遊ぼう ~C#er も TypeScript で楽々 WebGL~
 
JavaScript 講習会 #1
JavaScript 講習会 #1JavaScript 講習会 #1
JavaScript 講習会 #1
 
関数プログラミング ことはじめ (再)
関数プログラミング ことはじめ (再)関数プログラミング ことはじめ (再)
関数プログラミング ことはじめ (再)
 
ラムダ計算入門
ラムダ計算入門ラムダ計算入門
ラムダ計算入門
 
関数型プログラミング in javascript
関数型プログラミング in javascript関数型プログラミング in javascript
関数型プログラミング in javascript
 
Sized Linear Algebra Package のチュートリアル
Sized Linear Algebra Package のチュートリアルSized Linear Algebra Package のチュートリアル
Sized Linear Algebra Package のチュートリアル
 
Lisp講義1
Lisp講義1Lisp講義1
Lisp講義1
 
AuxパターンをDottyで解決する
AuxパターンをDottyで解決するAuxパターンをDottyで解決する
AuxパターンをDottyで解決する
 
純粋関数型アルゴリズム入門
純粋関数型アルゴリズム入門純粋関数型アルゴリズム入門
純粋関数型アルゴリズム入門
 
プログラミング言語Scala
プログラミング言語Scalaプログラミング言語Scala
プログラミング言語Scala
 
関数プログラミング入門
関数プログラミング入門関数プログラミング入門
関数プログラミング入門
 
C#を始めたばかりの人へのLINQ to Objects
C#を始めたばかりの人へのLINQ to ObjectsC#を始めたばかりの人へのLINQ to Objects
C#を始めたばかりの人へのLINQ to Objects
 
型クラス
型クラス型クラス
型クラス
 
Clojure programming-chapter-2
Clojure programming-chapter-2Clojure programming-chapter-2
Clojure programming-chapter-2
 
Lambda in template_final
Lambda in template_finalLambda in template_final
Lambda in template_final
 
Control.Arrow
Control.ArrowControl.Arrow
Control.Arrow
 
Java初心者勉強会(2015/08/07)資料
Java初心者勉強会(2015/08/07)資料Java初心者勉強会(2015/08/07)資料
Java初心者勉強会(2015/08/07)資料
 
Programming Haskell Chapter 11 切符番号選び
Programming Haskell Chapter 11 切符番号選びProgramming Haskell Chapter 11 切符番号選び
Programming Haskell Chapter 11 切符番号選び
 
Swift 3 で新しくなったところ - 表面から見えにくいところを中心に紹介 #ISAOcorp
Swift 3 で新しくなったところ - 表面から見えにくいところを中心に紹介 #ISAOcorpSwift 3 で新しくなったところ - 表面から見えにくいところを中心に紹介 #ISAOcorp
Swift 3 で新しくなったところ - 表面から見えにくいところを中心に紹介 #ISAOcorp
 
解説#74 連結リスト
解説#74 連結リスト解説#74 連結リスト
解説#74 連結リスト
 

Semelhante a Scalaによる型安全なエラーハンドリング

Sns suite presentation
Sns suite presentationSns suite presentation
Sns suite presentation
Jason Namkung
 
Real World OCamlを読んでLispと協調してみた
Real World OCamlを読んでLispと協調してみたReal World OCamlを読んでLispと協調してみた
Real World OCamlを読んでLispと協調してみた
blackenedgold
 

Semelhante a Scalaによる型安全なエラーハンドリング (20)

20141128 iOSチーム勉強会 My Sweet Swift
20141128 iOSチーム勉強会 My Sweet Swift20141128 iOSチーム勉強会 My Sweet Swift
20141128 iOSチーム勉強会 My Sweet Swift
 
F#入門 ~関数プログラミングとは何か~
F#入門 ~関数プログラミングとは何か~F#入門 ~関数プログラミングとは何か~
F#入門 ~関数プログラミングとは何か~
 
Implicit Explicit Scala
Implicit Explicit ScalaImplicit Explicit Scala
Implicit Explicit Scala
 
MP in Scala
MP in ScalaMP in Scala
MP in Scala
 
Livesense tech night immutable-js at a glance
Livesense tech night   immutable-js at a glanceLivesense tech night   immutable-js at a glance
Livesense tech night immutable-js at a glance
 
PRML 第4章
PRML 第4章PRML 第4章
PRML 第4章
 
ECMAScript 6 Features(PDF 版)
ECMAScript 6 Features(PDF 版)ECMAScript 6 Features(PDF 版)
ECMAScript 6 Features(PDF 版)
 
関数プログラミングことはじめ
関数プログラミングことはじめ関数プログラミングことはじめ
関数プログラミングことはじめ
 
Chapter 6: Computing on the language (R Language Definition)
Chapter 6: Computing on the language (R Language Definition)Chapter 6: Computing on the language (R Language Definition)
Chapter 6: Computing on the language (R Language Definition)
 
たのしい関数型
たのしい関数型たのしい関数型
たのしい関数型
 
(Ruby使いのための)Scalaで学ぶ関数型プログラミング
(Ruby使いのための)Scalaで学ぶ関数型プログラミング(Ruby使いのための)Scalaで学ぶ関数型プログラミング
(Ruby使いのための)Scalaで学ぶ関数型プログラミング
 
ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14
 
Sns suite presentation
Sns suite presentationSns suite presentation
Sns suite presentation
 
Actor&stm
Actor&stmActor&stm
Actor&stm
 
東京都市大学 データ解析入門 7 回帰分析とモデル選択 2
東京都市大学 データ解析入門 7 回帰分析とモデル選択 2東京都市大学 データ解析入門 7 回帰分析とモデル選択 2
東京都市大学 データ解析入門 7 回帰分析とモデル選択 2
 
R_note_01_ver1.1
R_note_01_ver1.1 R_note_01_ver1.1
R_note_01_ver1.1
 
Real World OCamlを読んでLispと協調してみた
Real World OCamlを読んでLispと協調してみたReal World OCamlを読んでLispと協調してみた
Real World OCamlを読んでLispと協調してみた
 
すごいHaskell楽しく学ぼう-第12章モノイド-
すごいHaskell楽しく学ぼう-第12章モノイド-すごいHaskell楽しく学ぼう-第12章モノイド-
すごいHaskell楽しく学ぼう-第12章モノイド-
 
Pythonintro
PythonintroPythonintro
Pythonintro
 
モナドハンズオン前座
モナドハンズオン前座モナドハンズオン前座
モナドハンズオン前座
 

Mais de TanUkkii

スケールするシステムにおけるエンティティの扱いと 分散ID生成
スケールするシステムにおけるエンティティの扱いと 分散ID生成スケールするシステムにおけるエンティティの扱いと 分散ID生成
スケールするシステムにおけるエンティティの扱いと 分散ID生成
TanUkkii
 
すべてのアクター プログラマーが知るべき 単一責務原則とは何か
すべてのアクター プログラマーが知るべき 単一責務原則とは何かすべてのアクター プログラマーが知るべき 単一責務原則とは何か
すべてのアクター プログラマーが知るべき 単一責務原則とは何か
TanUkkii
 

Mais de TanUkkii (15)

Distributed ID generator in ChatWork
Distributed ID generator in ChatWorkDistributed ID generator in ChatWork
Distributed ID generator in ChatWork
 
Non-blocking IO to tame distributed systems ー How and why ChatWork uses async...
Non-blocking IO to tame distributed systems ー How and why ChatWork uses async...Non-blocking IO to tame distributed systems ー How and why ChatWork uses async...
Non-blocking IO to tame distributed systems ー How and why ChatWork uses async...
 
Architecture of Falcon, a new chat messaging backend system build on Scala
Architecture of Falcon,  a new chat messaging backend system  build on ScalaArchitecture of Falcon,  a new chat messaging backend system  build on Scala
Architecture of Falcon, a new chat messaging backend system build on Scala
 
JSON CRDT
JSON CRDTJSON CRDT
JSON CRDT
 
Akka Clusterの耐障害設計
Akka Clusterの耐障害設計Akka Clusterの耐障害設計
Akka Clusterの耐障害設計
 
WaveNet
WaveNetWaveNet
WaveNet
 
スケールするシステムにおけるエンティティの扱いと 分散ID生成
スケールするシステムにおけるエンティティの扱いと 分散ID生成スケールするシステムにおけるエンティティの扱いと 分散ID生成
スケールするシステムにおけるエンティティの扱いと 分散ID生成
 
Akka HTTP
Akka HTTPAkka HTTP
Akka HTTP
 
すべてのアクター プログラマーが知るべき 単一責務原則とは何か
すべてのアクター プログラマーが知るべき 単一責務原則とは何かすべてのアクター プログラマーが知るべき 単一責務原則とは何か
すべてのアクター プログラマーが知るべき 単一責務原則とは何か
 
ディープニューラルネット入門
ディープニューラルネット入門ディープニューラルネット入門
ディープニューラルネット入門
 
プログラミング言語のパラダイムシフト(ダイジェスト)ーScalaから見る関数型と並列性時代の幕開けー
プログラミング言語のパラダイムシフト(ダイジェスト)ーScalaから見る関数型と並列性時代の幕開けープログラミング言語のパラダイムシフト(ダイジェスト)ーScalaから見る関数型と並列性時代の幕開けー
プログラミング言語のパラダイムシフト(ダイジェスト)ーScalaから見る関数型と並列性時代の幕開けー
 
プログラミング言語のパラダイムシフトーScalaから見る関数型と並列性時代の幕開けー
プログラミング言語のパラダイムシフトーScalaから見る関数型と並列性時代の幕開けープログラミング言語のパラダイムシフトーScalaから見る関数型と並列性時代の幕開けー
プログラミング言語のパラダイムシフトーScalaから見る関数型と並列性時代の幕開けー
 
Isomorphic web development with scala and scala.js
Isomorphic web development  with scala and scala.jsIsomorphic web development  with scala and scala.js
Isomorphic web development with scala and scala.js
 
ECMAScript6による関数型プログラミング
ECMAScript6による関数型プログラミングECMAScript6による関数型プログラミング
ECMAScript6による関数型プログラミング
 
これからのJavaScriptー関数型プログラミングとECMAScript6
これからのJavaScriptー関数型プログラミングとECMAScript6これからのJavaScriptー関数型プログラミングとECMAScript6
これからのJavaScriptー関数型プログラミングとECMAScript6
 

Scalaによる型安全なエラーハンドリング

  • 2. 型システムを崩壊させるもの • 例外 • 部分関数:一部の入力が関数のドメインに含まれて いない関数
  • 3. 例外の危険性 • 参照透明ではない • 型安全ではない def willThrow[A,B](a: A): B = throw new Exception • 高階関数との相性が悪い def highOrder[A,B](f: A => B) = { //fが例外を投げるとしたらどう対処するのか?? } ⇒どのtry式に属するかで結果が変わる ↓この関数の型のどこに例外の情報が?
  • 4. 関数型プログラミングでは 例外の代わりに 失敗を表現する型と その値を用いる ※Scalaはオブジェクト指向言語でもあるので、例外は存在する
  • 5. Option型 成功したときの値をもつSomeと、 失敗を表すNoneからなる型 sealed abstract class Option[+A] ! case class Some[+A](x: A) extends Option[A] ! case object None extends Option[Nothing]
  • 6. 1 sealed abstract class Option[+A] { 2 //値があるか判定 3 def isEmpty: Boolean 4 //値を取得 5 def get: A 6 //値を取得し、取得できなかった場合はデフォルト値を使用 7 final def getOrElse[B >: A](default: => B): B 8 //値に対してfを適用したOptionを返す 9 final def map[B](f: A => B): Option[B] 10 //値に対して失敗する可能性のあるfを適用したOptionを返す 11 final def flatMap[B](f: A => Option[B]): Option[B] 12 //述語関数を満たす値のOptionを取得 13 final def filter(p: A => Boolean): Option[A] 14 //値がなければデフォルトのOptionを返す 15 final def orElse[B >: A](alternative: => Option[B]): Option[B] 16 } 17 18 final case class Some[+A](x: A) extends Option[A] { 19 def isEmpty = false 20 def get = x 21 } 22 23 case object None extends Option[Nothing] { 24 def isEmpty = true 25 def get = throw new NoSuchElementException("None.get") 26 }
  • 7. Optionを使った関数 Listの先頭要素を返す関数headは 空のList()で未定義な部分関数だ 1 def head[T](xs: List[T]) = xs.head 2 3 scala> head(List()) 4 // java.util.NoSuchElementException: head of empty list 5 // at scala.collection.immutable.Nil$.head(List.scala:420) 6 // at scala.collection.immutable.Nil$.head(List.scala:417) 7 // at .head(<console>:7) 8 // ... 33 elided 9 10 def head[T](xs: List[T]) = if(xs.isEmpty) None else Some(xs.head) 11 12 scala> head(List()) 13 // res1: Option[Nothing] = None 14 15 scala> head(List(1,2,3)) 16 res2: Option[Int] = Some(1) ←コンパイルは通る ←要素のないリストでは実行時エラー ←Optionを使う ←失敗した場合はNoneが返る ←成功した場合はSomeが返る
  • 8. Optionから値を取り出す 1 scala> val odd = head(List(1,2,3)) .filter(_ % 2 == 1).map(_*3).getOrElse(1) 2 //odd: Int = 3 3 scala> val even = head(List(1,2,3)) .filter(_ % 2 == 0).map(_*2).getOrElse(0) 4 //even: Int = 0 5 scala> val none = head(List[Int]()) .filter(_ % 2 == 0).map(_*2).getOrElse(0) 6 //none: Int = 0 7 8 val matched = head(List(1,2,3)) match { 9 case Some(x) => x 10 case None => 0 11} 12 matched: Int = 1 いずれもエラーにはならない
  • 9. Optionは部分関数を 全関数にする def head[T](xs: List[T]): Int List[Int] Int List() def head[T](xs: List[T]): Option[Int] List[Int] Option[Int] List() 関数型プログラミングでは全関数になるように設計する
  • 10. PartialFunction型 部分関数を表すPartialFunction型を作れる ←パターンマッチによる 先頭要素取り出し ケースシーケンスを使えば 1 val head: PartialFunction[List[Int], Int] = { 2 case x::xs => x 3 } 4 3 val head2: Function1[List[Int], Int] = { 4 case x::xs => x 5 } 6 //<console>:12: warning: match may not be exhaustive. 7 //It would fail on the following input: Nil 5 6 head(List(1,2,3)) 7 //res1: Int = 1 9 10 head.isDefinedAt(List()) 11 //res2: Boolean = false 13 14 val optioned = head.lift 15 optioned(List()) 16 //res3: Option[Int] = None PartialFunction型以外では 部分関数を渡すと警告 ←入力が関数のドメインに 含まれるか判定 ←Optionを返す関数に変換
  • 11. Optionの合成 複数の値が失敗する可能性がある場合どうする? def optionPlus(a: Option[Int], b: Option[Int]): Int = ??? 例)2つの数字文字列を合計する 1 def stringToInt(s: String) = try { 2 Some(s.toInt) 3 } catch { case e: Exception => None } 8 11 def map2[A,B,C](oa: Option[A], ob: Option[B])(f: (A,B) => C) = oa.flatMap(a => ob.map(b => f(a,b))) 12 13 val optionPlus = map2(_: Option[Int], _: Option[Int])(_ + _) 14 22 val one = stringToInt(“1") //Some(1) 23 val two = stringToInt(“2") //Some(2) 24 val fail = stringToInt(“hello") //None 25 26 /*scala>*/ optionPlus(one,two) mapとflatMapを 27 //res1: Option[Int] = Some(3) 29 /*scala>*/ optionPlus(one,fail) 使えばできる 30 //res2: Option[Int] = None
  • 12. for内包表記による合成 1 val one = stringToInt("1") 2 val two = stringToInt("2") 3 val fail = stringToInt("hello") 4 5 for { 6 o <- one 7 t <- two 8 } yield o + t 9 //res1: Option[Int] = Some(3) 10 11 for { 12 o <- one 13 f <- fail 14 } yield o + f 15 //res2: Option[Int] = None 16 17 for { 18 o <- one 19 t <- two 20 th <- optionPlus(one,two) 21 } yield o + t + th 22 //res3: Option[Int] = Some(6) 左の式は右と等価 one.flatMap(o => two.map(t => o + t)) //res1: Option[Int] = Some(3) ! ! ! one.flatMap(o => fail.map(f => o + f)) //res2: Option[Int] = None ! ! ! one.flatMap(o => two.flatMap(t => optionPlus(one,two).map(th => o + t + th))) //res3: Option[Int] = Some(6)
  • 13. Optionの仲間 • Either[+A, +B]:成功の情報をRightで、失敗の情 報をLeftで表現する • Try[+T]:例外を投げる可能性のある処理を抽象化 し、SuccessとFailureによってその結果を表現す る
  • 14. おまけ)非同期処理 Optionで見たmapやforによる合成が普遍的 に行える例としてFutureを見ていこう Future[+T]: 非同期処理を扱い、未来の値を表現する 1 import scala.concurrent._ 2 import ExecutionContext.Implicits.global 3 import scala.util.{Success, Failure} 4 5 val f: Future[List[Int]] = Future { 6 Thread.sleep(5000) 7 List(1,2,3,4) 8 } 9 Try[+T] 10 f onComplete { 11 case Success(list) => list.foreach(println) 12 case Failure(e) => println(e.getMessage) 13 } 14 //1 2 3 4
  • 15. ネスト 5 val f: Future[List[Int]] = Future { 6 Thread.sleep(5000) 7 List(1,2,3,4) 8 } 14 15 f onComplete { 16 case Success(list) => { 17 val f2 = Future { 18 Thread.sleep(5000) 19 List(1,2,3,4) 20 } 21 f2 onComplete { 22 case Success(list2) => for (l1 <- list; l2 <- list2) println(l1 * l2) 23 case Failure(e) => println(e.getMessage) 24 } 25 } 26 case Failure(e) => println(e.getMessage) 27 } 28 //1 2 3 4 2 4 6 8 3 6 9 12 4 8 12 16
  • 16. ネストの回避(map) def map[S](f: (T) => S)(implicit executor: ExecutionContext) : Future[S] 5 val f: Future[List[Int]] = Future { 6 Thread.sleep(5000) 7 List(1,2,3,4) 8 } 9 31 val f2: Future[List[Int]] = f.map(list => { 32 Thread.sleep(5000) 33 val list2 = List(1,2,3,4) 34 for (l1 <- list; l2 <- list2) yield l1 * l2 35 }) 36 37 f2 onSuccess { 38 case res => res.foreach(println) 39 } 28 //1 2 3 4 2 4 6 8 3 6 9 12 4 8 12 16
  • 17. ネストの回避(for) 41 val f1: Future[List[Int]] = Future { 42 Thread.sleep(5000) 43 List(1,2,3,4) 44 } 45 46 val f2: Future[List[Int]] = Future { 47 Thread.sleep(5000) 48 List(1,2,3,4) 49 } 50 51 val f = for { 52 res1 <- f1 53 res2 <- f2 54 } yield for (l1 <- res1; l2 <- res2) yield l1 * l2 55 56 f onSuccess { 57 case res => res.foreach(println) 58 } //1 2 3 4 2 4 6 8 3 6 9 12 4 8 12 16
  • 18. まとめ • 例外と部分関数は型システムをすり抜け、実行時に 問題を起こす • Scalaでは例外ではなくOption、Either、Tryのよ うな値を使う • Optionによって部分関数を全関数にできる • Scalaのfor内包表記はすべてのモナドを扱える強力 な記法である