LoginSignup
6
2

More than 5 years have passed since last update.

任意の数の引数を取れる関数のデータ型を考えてたらモナドになった

Posted at

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

うまくいった。:relaxed:
ここで思った。
これ、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")

なにか役に立つ使い方が思いつけばまた記事にします。

もし、すでにこれと同じ構造のモナドのパッケージがあれば教えてください

6
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2