func1とfunc2を同じ型にしたい。
func1 :: r -> a
func2 :: r -> r -> a
リストで引数渡したら実行できるといいな。
引数が足りなかったらエラーにでもしようかな。
data ManyArgsFunc r a = ????
run :: ManyArgsFunc r a -> [r] -> a
Haskellといえば再帰
data ManyArgsFunc r a = Pure a | Func (r -> ManyArgsFunc r a)
func1とfunc2を変換できるか確認
あと実行関数が思い通りになるか
toManyArgsFunc1 :: (r -> a) -> ManyArgsFunc r a
toManyArgsFunc1 func1 = Func $ \r -> Pure (func1 r)
toManyArgsFunc2 :: (r -> r -> a) -> ManyArgsFunc r a
toManyArgsFunc2 func2 = Func $ \r1 ->
Func $ \r2 -> Pure (func2 r1 r2)
-- リストの引数が関数に順番に渡って、Pureまでくれば値を返す。
-- 途中で引数がなくなったらエラー
run :: ManyArgsFunc r a -> [r] -> a
run (Pure a) _ = a
run (Func f) (r:rs) = run (f r) rs
うまくいった。
ここで思った。
これ、Freeモナドに似てるのでモナドになるのでは。
instance Functor (ManyArgsFunc r) where
fmap f (Pure a) = Pure (f a)
fmap f (Func g) = Func $ \r -> fmap f (g r)
instance Applicative (ManyArgsFunc r) where
pure a = Pure a
(Pure f) <*> x = fmap f x
(Func g) <*> x = Func $ \r -> (g r) <*> x
instance Monad (ManyArgsFunc r) where
return a = Pure a
(Pure a) >>= f = f a
(Func g) >>= f = Func $ \r -> (g r) >>= f
やったーモナドになったー
挙動としてはこんなかんじ
func1 :: ManyArgsFunc Int String
func1 = Func $ \i -> Func $ \j -> Pure $ show (i + j)
trace :: (Show r , Show a) => ManyArgsFunc r a -> [r] -> String
trace (Pure a) _ = "Pure " ++ show a
trace (Func f) (r:rs) = "Func $ " ++ show r ++ " -> " ++ trace (f r) rs
action :: ManyArgsFunc Int (String,String)
action = do
a <- func1
b <- func1
return (a,b)
>>> run action [1..]
("3","7")
>>> trace action [1..]
Func $ 1 -> Func $ 2 -> Func $ 3 -> Func $ 4 -> Pure ("3","7")
なにか役に立つ使い方が思いつけばまた記事にします。
もし、すでにこれと同じ構造のモナドのパッケージがあれば教えてください