Applicative
Applicative
型クラスは、Functor
型クラスの拡張であり、Monad
型クラスよりは制限された中間的な構造で、関数適用を任意の計算効果に対して一般化したものであると見なすことができます。
定義
Applicative
型クラスは、大雑把に書けば次のように定義されています。(実際の定義はもっと複雑です)
class Applicative.{u, v} (f : Type u → Type v) extends Functor f where
/-- `a : α` が与えられたとき、`pure a : f α` は「何もせずに `a` を返すアクション」を表す。 -/
pure {α : Type u} : α → f α
/--
`<*>` 演算子の実装。
モナドにおいては、`mf <*> mx` は `do let f ← mf; x ← mx; pure (f x)` と同じになる。
つまり、まず関数を評価し、次に引数を評価して適用する。
予期しない順序で評価されることを避けるために、`mx` は `Unit → f α` という関数を使って遅延的に取得される。
-/
seq : {α β : Type u} → f (α → β) → (Unit → f α) → f β
map := fun x y => seq (pure x) (fun _ => y) -- デフォルト実装
Functor との違い
Functor.map
メソッドは (α → β) → F α → F β
という型を持ちます。これは、F = Id
の場合を考えてみると分かるように、1引数の関数適用を一般化したものだと考えることができます。では2引数、3引数の時はどうなるでしょうか?
単純に拡張すると、2引数の時は (α → β → γ) → F α → F β → F γ
という型になり、3引数の時は (α → β → γ → δ) → F α → F β → F γ → F δ
という型になります。これらを Functor.map
を使って表現するのは困難です。
しかし、F
が Applicative
型クラスのインスタンスになっていれば、n 引数の場合でも表現することができます。1
variable {α β γ δ : Type}
variable {F : Type → Type} [Applicative F]
/-- 1引数の場合 -/
example : (α → β) → F α → F β := fun f x =>
pure f <*> x
/-- 2引数の場合 -/
example : (α → β → γ) → F α → F β → F γ := fun f x y =>
pure f <*> x <*> y
/-- 3引数の場合 -/
example : (α → β → γ → δ) → F α → F β → F γ → F δ := fun f x y z =>
pure f <*> x <*> y <*> z
-
ここでの説明は 「プログラミングHaskell 第2版」(Graham Hutton 著、山本和彦訳)を参考にしました。 ↩