6. 関数型⾔言語との対応
Scala は
1. 関数型⾔言語のパーツをJavaのパーツにマッピングして
2. 使いやすいようにシンタックスなどを整えた⾔言語
パーツ ML Scala
関数 関数 メソッド
関数オブジェクト
代数データ型 ヴァリアント
レコード
case class
case object
モジュールシステム モジュール
ファンクタ
object
class
※ Haskellは型クラスによりMLのモジュールシステムと同等の機能を実現可能
(参考)
ML Modules and Haskell Type Classes: A Constructive Comparison
http://www.cse.unsw.edu.au/~chak/papers/modules-classes.pdf
7. 関数型⾔言語との対応
Scala は
1. 関数型⾔言語のパーツをJavaのパーツにマッピングして
2. 使いやすいようにシンタックスなどを整えた⾔言語
パーツ ML Scala
関数 関数 メソッド
関数オブジェクト
代数データ型 ヴァリアント
レコード
case class
case object
モジュールシステム モジュール
ファンクタ
object
class
※ Haskellは型クラスによりMLのモジュールシステムと同等の機能を実現可能
(参考)
ML Modules and Haskell Type Classes: A Constructive Comparison
http://www.cse.unsw.edu.au/~chak/papers/modules-classes.pdf
15.
object
Parser
{
sealed
abstract
class
Action
case
class
GoTo(place:
String)
extends
Action
case
object
Sleep
extends
Action
sealed
abstract
class
State
case
object
NoInput
extends
State
case
class
Entering(action:
Action,
count:
Int)
extends
State
case
class
Completed(action:
Action)
extends
State
case
object
WrongInput
extends
State
val
menu:
Map[String,
Action]
=
Map("hungry"
-‐>
GoTo("restaurant"),
"tired"
-‐>
Sleep,
...)
def
parse(String
input):
State
=
...
def
maxMenuLength:
Int
=
...
}
Parser.parse("hun")
//
→
Parser.Entering(Parser.GoTo("restaurant"),
3)
Parser.parse("hunt")
//
→
Parser.WrongInput
#1 objectでモジュールを作る
16.
object
Parser
{
sealed
abstract
class
Action
case
class
GoTo(place:
String)
extends
Action
case
object
Sleep
extends
Action
sealed
abstract
class
State
case
object
NoInput
extends
State
case
class
Entering(action:
Action,
count:
Int)
extends
State
case
class
Completed(action:
Action)
extends
State
case
object
WrongInput
extends
State
val
menu:
Map[String,
Action]
=
Map("hungry"
-‐>
GoTo("restaurant"),
"tired"
-‐>
Sleep,
...)
def
parse(String
input):
State
=
...
def
maxMenuLength:
Int
=
...
}
Parser.parse("hun")
//
→
Parser.Entering(Parser.GoTo("restaurant"),
3)
Parser.parse("hunt")
//
→
Parser.WrongInput
Parser モジュールの関数を呼び出す
Parser モジュールを作って
#1 objectでモジュールを作る
17.
object
Parser
{
sealed
abstract
class
Action
case
class
GoTo(place:
String)
extends
Action
case
object
Sleep
extends
Action
sealed
abstract
class
State
case
object
NoInput
extends
State
case
class
Entering(action:
Action,
count:
Int)
extends
State
case
class
Completed(action:
Action)
extends
State
case
object
WrongInput
extends
State
val
menu:
Map[String,
Action]
=
Map("hungry"
-‐>
GoTo("restaurant"),
"tired"
-‐>
Sleep,
...)
def
parse(String
input):
State
=
...
def
maxMenuLength:
Int
=
...
}
Parser.parse("hun")
//
→
Parser.Entering(Parser.GoTo("restaurant"),
3)
Parser.parse("hunt")
//
→
Parser.WrongInput
代数データ型の定義
#1 objectでモジュールを作る
18.
object
Parser
{
sealed
abstract
class
Action
case
class
GoTo(place:
String)
extends
Action
case
object
Sleep
extends
Action
sealed
abstract
class
State
case
object
NoInput
extends
State
case
class
Entering(action:
Action,
count:
Int)
extends
State
case
class
Completed(action:
Action)
extends
State
case
object
WrongInput
extends
State
val
menu:
Map[String,
Action]
=
Map("hungry"
-‐>
GoTo("restaurant"),
"tired"
-‐>
Sleep,
...)
def
parse(String
input):
State
=
...
def
maxMenuLength:
Int
=
...
}
Parser.parse("hun")
//
→
Parser.Entering(Parser.GoTo("restaurant"),
3)
Parser.parse("hunt")
//
→
Parser.WrongInput
⽂文字列列と動作の対応を
記述するマップ
#1 objectでモジュールを作る
関数の定義
19.
object
Parser
{
sealed
abstract
class
Action
case
class
GoTo(place:
String)
extends
Action
case
object
Sleep
extends
Action
sealed
abstract
class
State
case
object
NoInput
extends
State
case
class
Entering(action:
Action,
count:
Int)
extends
State
case
class
Completed(action:
Action)
extends
State
case
object
WrongInput
extends
State
val
menu:
Map[String,
Action]
=
Map("hungry"
-‐>
GoTo("restaurant"),
"tired"
-‐>
Sleep,
...)
def
parse(String
input):
State
=
...
def
maxMenuLength:
Int
=
...
}
Parser.parse("hun")
//
→
Parser.Entering(Parser.GoTo("restaurant"),
3)
Parser.parse("hunt")
//
→
Parser.WrongInput
#1 objectでモジュールを作る
メニューを外部から
注⼊入したい
20.
sealed
abstract
class
Action
case
class
GoTo(place:
String)
extends
Action
case
object
Sleep
extends
Action
class
Parser(menu:
Map[String,
Action])
{
sealed
abstract
class
State
case
object
NoInput
extends
State
case
class
Entering(action:
Action,
count:
Int)
extends
State
case
class
Completed(action:
Action)
extends
State
case
object
WrongInput
extends
State
def
parse(String
input):
State
=
...
def
maxMenuLength:
Int
=
...
}
val
parser
=
new
Parser(
Map("hungry"
-‐>
GoTo("restaurant"),
"tired"
-‐>
Sleep,
...))
parser.parse("hun")
//
→
parser.Entering(GoTo("restaurant"),
3)
parser.parse("hunt")
//
→
parser.WrongInput
#2 メニューをパラメータとして渡す
21.
sealed
abstract
class
Action
case
class
GoTo(place:
String)
extends
Action
case
object
Sleep
extends
Action
class
Parser(menu:
Map[String,
Action])
{
sealed
abstract
class
State
case
object
NoInput
extends
State
case
class
Entering(action:
Action,
count:
Int)
extends
State
case
class
Completed(action:
Action)
extends
State
case
object
WrongInput
extends
State
def
parse(String
input):
State
=
...
def
maxMenuLength:
Int
=
...
}
val
parser
=
new
Parser(
Map("hungry"
-‐>
GoTo("restaurant"),
"tired"
-‐>
Sleep,
...))
parser.parse("hun")
//
→
parser.Entering(GoTo("restaurant"),
3)
parser.parse("hunt")
//
→
parser.WrongInput
#2 メニューをパラメータとして渡す
classにして外からメニュー
を渡せるように
インスタンス化して
モジュールを⽣生成
22.
sealed
abstract
class
Action
case
class
GoTo(place:
String)
extends
Action
case
object
Sleep
extends
Action
class
Parser(menu:
Map[String,
Action])
{
sealed
abstract
class
State
case
object
NoInput
extends
State
case
class
Entering(action:
Action,
count:
Int)
extends
State
case
class
Completed(action:
Action)
extends
State
case
object
WrongInput
extends
State
def
parse(String
input):
State
=
...
def
maxMenuLength:
Int
=
...
}
val
parser
=
new
Parser(
Map("hungry"
-‐>
GoTo("restaurant"),
"tired"
-‐>
Sleep,
...))
parser.parse("hun")
//
→
parser.Entering(GoTo("restaurant"),
3)
parser.parse("hunt")
//
→
parser.WrongInput
#2 メニューをパラメータとして渡す
Action型の内容は
Parserの処理理に依存しない
これも外に出したい
23.
sealed
abstract
class
Action
case
class
GoTo(place:
String)
extends
Action
case
object
Sleep
extends
Action
class
Parser[A](menu:
Map[String,
A])
{
sealed
abstract
class
State
case
object
NoInput
extends
State
case
class
Entering(action:
A,
count:
Int)
extends
State
case
class
Completed(action:
A)
extends
State
case
object
WrongInput
extends
State
def
parse(String
input):
State
=
...
def
maxMenuLength:
Int
=
...
}
val
parser:
Parser[Action]
=
new
Parser(
Map("hungry"
-‐>
GoTo("restaurant"),
"tired"
-‐>
Sleep,
...))
parser.parse("hun")
//
→
parser.Entering(GoTo("restaurant"),
3)
parser.parse("hunt")
//
→
parser.WrongInput
#3 型も外から指定するように
24.
sealed
abstract
class
Action
case
class
GoTo(place:
String)
extends
Action
case
object
Sleep
extends
Action
class
Parser[A](menu:
Map[String,
A])
{
sealed
abstract
class
State
case
object
NoInput
extends
State
case
class
Entering(action:
A,
count:
Int)
extends
State
case
class
Completed(action:
A)
extends
State
case
object
WrongInput
extends
State
def
parse(String
input):
State
=
...
def
maxMenuLength:
Int
=
...
}
val
parser:
Parser[Action]
=
new
Parser(
Map("hungry"
-‐>
GoTo("restaurant"),
"tired"
-‐>
Sleep,
...))
parser.parse("hun")
//
→
parser.Entering(GoTo("restaurant"),
3)
parser.parse("hunt")
//
→
parser.WrongInput
#3 型も外から指定するように
Parserモジュールは任意の型
で受け取れる
型パラメータがついた
25. abstract
class
Parser
{
type
A
def
menu:
Map[String,
A]
sealed
abstract
class
State
case
object
NoInput
extends
State
case
class
Entering(action:
A,
count:
Int)
extends
State
case
class
Completed(action:
A)
extends
State
case
object
WrongInput
extends
State
def
parse(String
input):
State
=
...
def
maxMenuLength:
Int
=
...
}
sealed
abstract
class
Action
case
class
GoTo(place:
String)
extends
Action
case
object
Sleep
extends
Action
val
parser:
Parser
=
new
Parser{
type
A
=
Action
val
menu
=
Map("hungry"
-‐>
GoTo("restaurant"),
"tired"
-‐>
Sleep,
...
)
}
parser.parse("hun")
//
→
parser.Entering(GoTo(Restaurant),
3)
parser.parse("hunt")
//
→
parser.WrongInput
#4
型パラメータを抽象型で
書き換えてみる
26. abstract
class
Parser
{
type
A
def
menu:
Map[String,
A]
sealed
abstract
class
State
case
object
NoInput
extends
State
case
class
Entering(action:
A,
count:
Int)
extends
State
case
class
Completed(action:
A)
extends
State
case
object
WrongInput
extends
State
def
parse(String
input):
State
=
...
def
maxMenuLength:
Int
=
...
}
sealed
abstract
class
Action
case
class
GoTo(place:
String)
extends
Action
case
object
Sleep
extends
Action
val
parser:
Parser
=
new
Parser{
type
A
=
Action
val
menu
=
Map("hungry"
-‐>
GoTo("restaurant"),
"tired"
-‐>
Sleep,
...
)
}
parser.parse("hun")
//
→
parser.Entering(GoTo(Restaurant),
3)
parser.parse("hunt")
//
→
parser.WrongInput
ここで型を指定
これが抽象型
具体的な型は⼦子クラスで
#4
型パラメータを抽象型で
書き換えてみる
27. abstract
class
Parser
{
type
A
def
menu:
Map[String,
A]
sealed
abstract
class
State
case
object
NoInput
extends
State
case
class
Entering(action:
A,
count:
Int)
extends
State
case
class
Completed(action:
A)
extends
State
case
object
WrongInput
extends
State
def
parse(String
input):
State
=
...
def
maxMenuLength:
Int
=
...
}
sealed
abstract
class
Action
case
class
GoTo(place:
String)
extends
Action
case
object
Sleep
extends
Action
val
parser:
Parser
=
new
Parser{
type
A
=
Action
val
menu
=
Map("hungry"
-‐>
GoTo("restaurant"),
"tired"
-‐>
Sleep,
...
)
}
parser.parse("hun")
//
→
parser.Entering(GoTo(Restaurant),
3)
parser.parse("hunt")
//
→
parser.WrongInput
型パラメータが消えた
#4
型パラメータを抽象型で
書き換えてみる
28. abstract
class
Parser
{
type
Action
def
menu:
Map[String,
Action]
sealed
abstract
class
State
case
object
NoInput
extends
State
case
class
Entering(action:
Action,
count:
Int)
extends
State
case
class
Completed(action:
Action)
extends
State
case
object
WrongInput
extends
State
def
parse(String
input):
State
=
...
def
maxMenuLength:
Int
=
...
}
val
parser:
Parser
=
new
Parser{
sealed
abstract
class
Action
case
class
GoTo(place:
String)
extends
Action
case
object
Sleep
extends
Action
val
menu
=
Map("hungry"
-‐>
GoTo("restaurant"),
"tired"
-‐>
Sleep,
...
)
}
parser.parse("hun")
//
→
parser.Entering(GoTo(Restaurant),
3)
parser.parse("hunt")
//
→
parser.WrongInput
#5
Action型の定義を
⼦子クラスに移す
29. abstract
class
Parser
{
type
Action
def
menu:
Map[String,
Action]
sealed
abstract
class
State
case
object
NoInput
extends
State
case
class
Entering(action:
Action,
count:
Int)
extends
State
case
class
Completed(action:
Action)
extends
State
case
object
WrongInput
extends
State
def
parse(String
input):
State
=
...
def
maxMenuLength:
Int
=
...
}
val
parser:
Parser
=
new
Parser{
sealed
abstract
class
Action
case
class
GoTo(place:
String)
extends
Action
case
object
Sleep
extends
Action
val
menu
=
Map("hungry"
-‐>
GoTo("restaurant"),
"tired"
-‐>
Sleep,
...
)
}
parser.parse("hun")
//
→
parser.Entering(GoTo(Restaurant),
3)
parser.parse("hunt")
//
→
parser.WrongInput
#5
Action型の定義を
⼦子クラスに移す
ここで抽象型の具体型を定義
30. abstract
class
Parser
{
type
Action
def
menu:
Map[String,
Action]
sealed
abstract
class
State
case
object
NoInput
extends
State
case
class
Entering(action:
Action,
count:
Int)
extends
State
case
class
Completed(action:
Action)
extends
State
case
object
WrongInput
extends
State
def
parse(String
input):
State
=
...
def
maxMenuLength:
Int
=
...
}
object
SomeParser
extends
Parser{
sealed
abstract
class
Action
case
class
GoTo(place:
String)
extends
Action
case
object
Sleep
extends
Action
val
menu
=
Map("hungry"
-‐>
GoTo("restaurant"),
"tired"
-‐>
Sleep,
...)
}
SomeParser.parse("hun")
//
→
SomeParser.Entering(GoTo("restaurant"),
3)
SomeParser.parse("hunt")//
→
SomeParser.WrongInput
#6 objectで定義する
31. パス依存型
• 別のモジュールに属する型は、別の型として扱われな
いと不不⾃自然
• コンパイラは型が属するオブジェクトを辿って、型が
⼀一致するかを判断する。これをパス依存型という。
• パス依存型により、モジュールとして素直に扱える。
(JavaのInner Classはパス依存型ではない)
val
someParser
=
new
Parser(...)
val
antotherParser
=
new
Parser(...)
someParser.parse(...)
//
→
someParser.During(...)
anotherParser.parse(...)
//
→
anotherParser.During(...)
//
以下はコンパイルエラー
val
state:
someParser.State
=
anotherParser.parse(...)
40. traitで分割
• ひとつのモジュールが⼤大きくなってきたら、trait
で分断できる
object
Hoge
{
}
trait
SomePart{
}
trait
AnotherPart{
}
//
traitを合成(mix-‐in)してモジュールを作る
object
Hoge
extends
SomePart
with
AnotherPart
Some Functions
Another Functions
Some Functions
Another Functions
41. 合成も簡単
• インスタンス⽣生成時にもmix-inできる
//
objectでモジュールを作る
object
Hoge
extends
SomePart
with
AnotherPart
//
インスタンス⽣生成時にmix-‐in
val
hoge
=
new
SomePart
with
AnotherPart
val
fuga
=
new
SomePart
with
YetAnotherPart
//
条件によって、mix-‐inされるものを切切り替える
val
foo
=
if
(config.availableXXX)
new
SomePart
with
XXX
else
new
SomePart
with
Default
42. ⾃自分型 (self type)
• traitが他のtraitに依存することを⾃自分型により明
⽰示する。
object
Hoge
{
}
trait
SomePart{
}
trait
AnotherPart{
this
:
SomePart
=>
}
object
Hoge
extends
SomePart
with
AnotherPart
Some Functions
Another Functions
Some Functions
Another Functions
Call
⾃自分型
mix-in時に⾃自分型を満たさないと
コンパイルエラー
SomePart内の
関数が使える
44. MLのsignatureとの対応
• MLではモジュールの型が、そのモジュールの外部に対する
インターフェースを⽰示す。
– モジュールの実装はstructure(struct)を、
– その型の指定はsignature(sig) を使う。
• 実装のないtraitはMLのsignatureに対応する
(*
OCamlのREPLで実⾏行行した場合
*)
# module Three = struct let x = 3 end;;
← 実装
module Three : sig val x : int end
← 型
# module type X_int = sig val x : int end;;
← 型
module type X_int = sig val x : int end
← 型
# module Increment (M : X_int) = struct let x = M.x + 1 end;;
← 実装
module Increment : functor (M : X_int) -> sig val x : int end
← 型
※「Real World Ocaml」Chapter 9 Functorより抜粋
45. その他
各種ライブラリでのtraitの⽤用例例
• DSLの語彙を増やす
//
ScalaTestの例例
//
ShouldMatcherをmix-‐inすると...
class
HogeSpec
extends
Specification
with
ShouldMatcher
{
//
中でshouldが使えるように
"hoge"
in
{
hoge
should
equal
(3)
}
//
Akka(Actorなどの並列列分散ライブラリ)の例例
//
ActorLoggingをmix-‐inすると...
class
HogeActor
extends
Actor
with
ActorLogging
{
//
受け取ったメッセージがログに出⼒力力される
def
receive
=
{
...
• 機能を付与する