SMLでApplicativeを書く
コードはcodeplexに置いてあります。
(sml/njでuse "applicative.use"; するとApplicativeなOptionが使えます))
http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Applicative.html を参考にしました。
とりあえず下のレイヤから書いていきます。
signature FUNCTOR_CORE = sig type 'a t val fmap : ('a -> 'b) -> 'a t -> 'b t end signature APPLICATIVE_CORE = sig include FUNCTOR_CORE (* ここにも type 'a t とか書きたいなぁ… *) val pure : 'a -> 'a t val <*> : ('a -> 'b) t * 'a t -> 'b t end
プリミティブなものだけを実装しておきます。(便利関数は後付けします。)
で、標準モジュール(structure)のOptionをAPPLICATIVE_CORE(Applicative+Functor)にします。
structure ApplicativeOption : APPLICATIVE_CORE = struct type 'a t = 'a Option.option fun fmap f = Option.map f fun pure x = SOME x fun <*> (SOME f, SOME x) = SOME (f x) | <*> (SOME _, NONE ) = NONE | <*> (NONE , _) = NONE end
fmap は引数がある(=SOMEの)ときのみ、その中身に対して関数fを適用する関数です。
関数 op<*> は 型t 上での関数適用を行います。
では本体。
functor MkApplicative(A : APPLICATIVE_CORE) = struct local fun const x _ = x fun id x = x in open A (* ---> SML/NJ workaround *) infix <*> (* SML/NJ workaround <--- *) structure Open = struct infix *> <* fun u *> v = pure (const id) <*> u <*> v fun u <* v = pure const <*> u <*> v val op<*> = op<*> fun <$> (f,a) = fmap f a fun <$ (a,b) = fmap (const a) b fun <**> (a,f) = f <*> a end open Open fun liftA f x = fmap f x fun liftA2 (f:'a->'b->'c) (x:'a t) (y:'b t) = (fmap f x) <*> y fun liftA3 (f:'a->'b->'c->'d) (x:'a t) (y:'b t) (z:'c t) = (fmap f x) <*> y <*> z end (* local *) end (* functor MkApplicative(A : APPLICATIVE_CORE) *) (* 名前をOptionにすると(Monadとかで)別に拡張した * Optionと被るのでメンドクサイ *) structure ApplicativeOption = MkApplicative(ApplicativeOption)
structure Openは Monad にしちゃう functor を通して今時の OCaml モジュールプログラミングを俯瞰 で解説がある*1open用名前空間です。片方の値を捨てる系の演算子は要らないような気もする…。
ところでApplicativeってなんでこんなに似たような中置演算子がいっぱいあるんでしょうか?(^^;;