モナドに関する Wadler の論文を読んだ (そしてそのいくつかのセクションをざっと読んだ) 後、私は論文をより綿密に調べ、彼が記述した各モナドのファンクターとアプリケーションのインスタンスを定義することにしました。型シノニムの使用
type M a = State -> (a, State)
type State = Int
Wadler は状態モナドを定義するために使用します。私は次のようにしています (関連する名前を使用して、後で newtype 宣言で定義できるようにします)。
fmap' :: (a -> b) -> M a -> M b
fmap' f m = \st -> let (a, s) = m st in (f a, s)
pure' :: a -> M a
pure' a = \st -> (a, st)
(<@>) :: M (a -> b) -> M a -> M b
sf <@> sv = \st -> let (f, st1) = sf st
(a, st2) = sv st1
in (f a, st2)
return' :: a -> M a
return' a = pure' a
bind :: M a -> (a -> M b) -> M b
m `bind` f = \st -> let (a, st1) = m st
(b, st2) = f a st1
in (b, st2)
newtype 宣言で型コンストラクターの使用に切り替えると、たとえば、
newtype S a = S (State -> (a, State))
すべてがバラバラになります。すべてがわずかな変更にすぎません。たとえば、
instance Functor S where
fmap f (S m) = S (\st -> let (a, s) = m st in (f a, s))
instance Applicative S where
pure a = S (\st -> (a, st))
ただし、ラムダ式がその型コンストラクター内に隠されているため、GHC では何も実行されません。今私が見る唯一の解決策は、関数を定義することです:
isntThisAnnoying s (S m) = m s
s を 'st' にバインドし、実際に値を返すには、たとえば、
fmap f m = S (\st -> let (a, s) = isntThisAnnoying st m in (f a, s))
これらの補助機能を使用しない別の方法はありますか?