top page > computer > haskell > web_lecture > for_programmer > applicative_class.html
更新日:
文責: 重城良国

型クラス: アプリカティブ

(工事中 60%)

はじめに

ファンクター、アプリカティブ、モナドについて見てきた。コンテナあるいは文脈の持つ性質だ。これらの性質は型クラスによって一般化できる。ファンクターを表す型クラスFunctorはすでに見た。ここでは型クラスApplicativeを示す。

関数fmapの別名

アプリカティブスタイルというコーディングの作法がある。たとえばモナドであったとしてもアプリカティブの範囲内で表現可能なアルゴリズムであればアプリカティブスタイルで書いたほうがきれいだ。モナドによる

mx `bind` \x ->
my `bind` \y ->
ret $ g x y

のような表現はアプリカティブスタイルで

pure g `app` mx `app` my

のように書ける。pure g `app`の部分は

g :: a -> b

app :: f (a -> b) -> f a -> f b

の第1引数とするために関数pureで文脈つきの関数に変換している。ファンクタ関数である

fmap :: (a -> b) -> f a -> f b

を使えば

g `fmap` mx `app` my

のように書ける。関数fmapや関数appを演算子的に使うことで読みやすくなる。それならばはじめから同じ意味の演算子を用意すればいい。関数fmapには同じ意味の演算子(<$>)が用意されている。うえの例ならば

(g <$> mx) `app` my

のように書ける。丸括弧が必要になってしまったが型クラスApplicativeを導入することでこれは不要になる。

演算子(<$>)はどこであれ関数fmapの代わりに使えるので、たとえばリストについても

(* 10) <$> [3, 5, 2, 4, 8]

のように使える。単純な関数適用

(* 10) $ 8

と比べてみよう。

型クラスApplicative

アプリカティブであるという性質を表現する型クラスApplicativeが用意されている。クラス関数は

pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b

のふたつだ。そして型クラスApplicativeのインスタンスが本当にアプリカティブであるためにはファンクタ則とモノイダル則を満たす必要がある。ファンクタ則は

fm :: (a -> b) -> f a -> f b
fm g = (pure g <*>)

と定義された関数fmについて

fm id == id

であり、モノイダル則は

unit :: f ()
unit = pure ()
(.**) :: f a -> f b -> f (a, b)
x .** y = pure (,) <*> x <*> y

と定義された値unitと演算子(.**)について

fm snd (unit .** v) == v
fm fst (u .** unit) == u
fm asl (u .** (v .** w)) == (u .** v) .** w
where asl (x, (y, z)) = ((x, y), z)

である。((), x)や(x, ())が情報としてはxとほとんど変わらないことや(x, (y, z))と((x, y), z)も同じようにほとんど変わらないことを考えるとこれらはほぼ「単位元を持ち」「結合則を満たす」ということだ。

アプリカティブであればファンクタである。よって型クラスApplicativeにはFunctorクラスの型クラス制約がつく。型クラスApplicativeの型クラス宣言は以下のようになる。

class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b

fmap == (<*>) . pure

ここで型クラスFunctorとApplicativeがそれぞれの規則を守って適切に定義されているならば必ずfmap == (<*>) . pureが成り立つことを示す。つまり型クラスApplicativeのクラス関数から組み立てられたファンクタ関数と型クラスFunctorのクラス関数であるファンクタ関数とが交換可能ということだ。

まず前提として

foo :: (a -> b) -> X a -> X b
bar :: (a -> b) -> X a -> X b

であるとき

g . h == p . q

を満たすすべてのg, h, p, qで

foo g . bar h == foo p . bar q

が成り立つということを示す。これは((a -> b) -> X a -> X b)型の関数においてなかみの値の変化は第1引数の(a -> b)型の関数にまかせるしかないことと文脈Xに関する変化は引数にかかわらず常に同じである必要があることから導ける。つまり、この型の関数では常になかみに対する変換と文脈に対する変換に直交性があるということだ。

h = id, p = idとするとg . id == id . qであり

g == qとなり、また、

foo g . bar id == foo id . bar q

ここで関数foo, barがともにファンクタ則を満たすとすると

foo g . id == id . bar g

となる。よって、すべての関数gに対して

foo g == bar g

だ。よって関数fooの型が(a -> b) -> X a -> X bであり、かつファンクタ則を満たすならば関数fooは一意に定まる。

よってともにファンクタ則を満たす関数fmapと(<*>) . pureはそれぞれに同じ引数を与えれば同じ値を返すという点で等しいと言える。

アプリカティブスタイル

型クラスApplicativeのクラス関数によるアプリカティブスタイルの例を見てみよう。

% ghci
Prelude> (*) <$> Just 3 <*> Just 10
Just 30
Prelude> (*) <$> Just 3 <*> Nothing
Nothing

関数(*)に複数の文脈つきの値をあたえているのがうまく表現されている。

まとめ

モナドであっても可能ならばアプリカティブスタイルで書くことでコードがわかりやすくすっきりとする。モナドはすべてアプリカティブだがアプリカティブのなかにはモナドにはできないものもある。前回は、そのうちの一例としてエラーメッセージをすべて結合するアプリカティブファンクターを示した。ファンクター、アプリカティブファンクター、モナドというふうにインターフェースの共通部分が増えるほどそれぞれの構造に独自の機能を持つ余地は狭くなる。

ここではファンクターであるという性質を示す型クラスFunctorに続いてアプリカティブであるという性質を示す型クラスApplicativeを示した。このクラスのインスタンスにするには型クラスFunctorのインスタンスにしたうえで、クラス関数pureとクラス演算子(<*>)を定義する。

メモ

とりあえずGHC-7.10以降を対象にした説明とする。つまりアプリカティブスタイルを使用するのにimport Control.Applicativeは不要とする。ただし出版前に状況を確認し初心者でもGHC-7.8以前をインストールしている可能性がどのくらいか、や7.10へのアップデートの敷居の高さを考慮して、注意書きを追加するかどうか決めよう。7.10以降への対応で良さそうだ。

「モナドではないアプリカティブ」へもどる 「型クラス: モナド」へ

正当なCSSです! HTML5 Powered with CSS3 / styling, and Semantics