Haskellを学ぶに当たって避けて通れないのがMonadですね(?)
しかしいきなりMonadを理解するのは難しいかと思います.
なので, まずFunctorとApplicativeを理解してみようというお話です.
FunctorとApplicativeがどうMonadと繋がるのか
モナドの定義をみてみましょう
class Applicative m => Monad (m :: * -> *) where
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
return :: a -> m a
fail :: String -> m a
またApplicativeの定義をみてみましょう
class (Functor f) => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
となっています.
Monad構造を持つものはApplicative構造を持っています.
またApplicative構造を持つものはFunctor構造を持っています.
Functor => Applicative => Monad
よってMonadを学ぶにはFunctorとApplicativeを理解する必要があると思います.
Functor
Functorとは?
Functorとは全体を写せる(map over)ものの型クラス
です.
よくわかりませんね.
一つ例をあげるとリストはFunctorに属します.
リストのメソッドにmap
というメソッドがあります. よく使われますね.
Swiftでの例をみてみましょう.
func double(x: Int) -> Int {
return x * 2
}
let x = [1,2,3]
let y = x.map(double) // [2,4,6]
mapはどのような動きをしているかというと, xの要素である1,2,3に対してdouble
を適用し, リストに包んで返しています.
つまり, この例では
全体: リストに包む
写す: doubleを適用
と対応が取れます.
これらからFunctorは元の構造を保ったまま, 関数が適用できるような構造
のことを指します.
Functorの実装を見る
HaskellのFunctor型クラスを見てみましょう.実装は以下のようになっています
class Functor f where
fmap :: (a -> b) -> f a -> f b
これだけです.それではfmapを詳しくみてみましょう.
fmapの第一引数は(a型の値をとってb型の値を返す関数)です.特に問題はないですね.
第二引数は(f a)となっています.
これはfという構造を持ったa型の値を表しています. リストで言えばf a = [] a = [a]
です.
結局のところ, fmapは何をするのかというと
fという構造を持ったもの(fはa型の値を持つ)に対して何か関数を適用して, fという構造を保ったまま返す
ということをしています.
Optional(Maybe)を実装してみる
さて、上で説明したことをOptional(Maybe)を使って実装してみます.
HaskellとSwiftの二つの言語で実装しました.
Haskell
まずデータ型を定義します.
値を持っていればSomeで値をラップし, 値がない場合はNoneとします.
data Optional a = Some a | None deriving (Eq, Ord, Read, Show)
OptionalはFunctorを準拠します.
Optionalが値を持っている(Some)ならば, Someの中の値に関数を適用して, 再度Someでラップします.
値を持っていない(None)ならば, Noneを返します. (何もないものに関数を適用しても何も起こりませんよね)
instance Functor Optional where
fmap f (Some x) = Some (f x)
fmap f None = None
さて確かめてみましょう.
*Main> x = Some 10
*Main> y = None
*Main> fmap (*2) x
Some 20
*Main> fmap (*2) y
None
うまく動いていますね.
Swift
SwiftではFunctor自体を定義します.
protocol Functor {
associatedtype A
associatedtype B
associatedtype F
func fmap(f: ((A) -> B)) -> F
}
次にOptionalを定義します.(名前が衝突するのでMyOptionalとしています)
また, MyOptional(T)に対してMyOptional(U)を返す方法がわからなかったので今回は同じ型を返すようにしています(MyOptional(T)に対してMyOptional(T)を返すようにしています)
enum MyOptional<T>: Functor {
typealias A = T
typealias B = T
typealias F = MyOptional
case some(value: T)
case none
func fmap(f: ((T) -> T)) -> F {
switch self {
case .some(let value):
return .some(value: f(value))
case .none:
return .none
}
}
}
さて確かめてみましょう.
let x = MyOptional<Int>.some(value: 1)
let y = MyOptional<Int>.none
let z = x.fmap(f: { x in return x * 2 })
let w = y.fmap(f: { x in return x * 2 })
print(x) // some(value: 1)
print(y) // none
print(z) // some(value: 2)
print(w) // none
うまく動いています.
Applicative
Applicativeとは
先ほどfmap (*2) (Some 1)
をFunctorの利用例としてあげました.
実はfmap (*) (Some 2)
もできます.
この型はfmap (*) (Some 2) :: Num a => Optional (a -> a)
となっています.
書き方を変えるとSome (2 *)
です. つまり, Functor値に関数を与えることもできます.
ではこれを踏まえて, Some (2 *)
と Some 3
があったとして, Some (2 *)
をSome 3
に適用できるでしょうか. 答えはNoです.
残念ながらFunctorだけの世界では, Some (2 *)
は使い物になりません.
ここでApplicativeが登場します.
Applicativeな世界ではSome (2 *)
をSome 3
に適用することができます.
つまり, (Some (2 *)) (Some 3) = Some 6
を得ることができる世界になるわけです.
Applicativeの実装をみる
HaskellのApplicative型クラスを見てみましょう.実装は以下のようになっています
class (Functor f) => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
詳しくみていきましょう.
まず1行目class (Functor f) => Applicative f
について,「Applicativeなf
はFunctor型クラスを準拠している必要がある.」という制約をつけています.
二行目以降は「Applicativeなf
はpure
と<*>
の実装を持つ必要がある」と言っています.
pure
は「ある値をfで包む, つまりApplicative値に変換する関数」,
<*>
は「fで包まれた(a->b)な関数を,fに包まれたa型の値に対して適用してfに包まれたbを返す」
となっています.
Optional(Maybe)を実装する
Functorの説明で実装したOptionalにApplicativeを準拠させます.
haskell
instance Applicative Optional where
pure = Some
None <*> _ = None
(Some f) <*> x = fmap f x
pure
はSomeで包みます. pure (*2)
とすると, (Some (*2))
が得られます.
<*>
は (Optional 関数) <*> (Optional 値)のように使います.
左引数(関数)がNoneの場合, 問答無用でNoneを返します.何かに無を適用しても無です.
左引数(関数)がSomeの場合, Some内の関数を取り出し, fmapを適用します. つまり両辺のSomeから値を取り出し, 適用しSomeに入れて返すということをしています.
確認してみましょう
*Main> Some (+3) <*> Some 2
Some 5
*Main> pure (*2) <*> Some 10
Some 20
*Main> pure (*2) <*> None
None
-- アプリカティブスタイル <*> を連続して使う
*Main> pure (+) <*> Some 3 <*> Some 5
Some 8
*Main> pure (+) <*> Some 3 <*> None
None
*Main> pure (+) <*> None <*> Some 5
None
Swift
Swiftでの実装は以下のようになります
Applicative型クラスを実装したいところでしたが, どうしたらいいかわからなかったのでMyOptionalをextensionしてpureと<*>を実装します. <*>はapplyとして実装します
extension MyOptional {
static func pure<U>(_ a: U) -> MyOptional<U> {
return .some(value: a)
}
func apply(_ f: MyOptional<((T) -> T)>) -> MyOptional<T> {
switch f {
case .none:
return .none
case .some(let fun):
return self.fmap(f: fun)
}
}
}
確認してみましょう.
let f1 = MyOptional<Int>.pure({ x in x+2 })
let f2 = MyOptional<Int>.pure({ x in x*3 })
let x = MyOptional<Int>.some(value: 2)
let y = x.apply(f1).apply(f2)
print(x) // some(value: 2)
print(y) // some(value: 12)
うまくいっているように見えます.
しかし残念ながら, swiftでCurry化は普通には使えないのでアプリカティブスタイルはかけませんでした.
Curry化を実装すれば, できそうです.
それは辛いので今回は実装しません.
あとがき
Haskellを書き始めて二ヶ月程度なので, 変なことを言っている可能性があります.
そういったところはマサカリを投げていただけると助かります.
参考文献
すごいHaskell楽しく学ぼう!