LoginSignup
0
0

More than 1 year has passed since last update.

※初歩的な部分

Functor

※復習 Functor整理

何かを写すモノという概念を型クラスとして定義したモノ。

fmap :: Functor f => (a -> b) -> f a -> f b

という関数で、f a を f b へと変換(写す)している。

ghci> fmap (\x -> x + 1) (Just 1) -- Just 2
ghci> fmap (\x -> x + 1) [1,2] -- [2,3]

MaybeもリストもFunctorのinstance

Applicative Functor

※復習 Applicative Functor 整理

Functorの拡張版。できることが多いFunctor。

pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b

--- Controll.Applicative
(<$>) :: Functor f => (a -> b) -> f a -> f b
f <$> x = fmap f x

上記関数をもっていて、関数にもFunctorの文脈を付けられる。※ここのfは型コンストラクタFunctor

ghci> pure (+) <*> Just 3 <*> Just 3 -- Just 6
ghci> (+) <$> Just 3 <*> Just 3 -- Just 6

モナド整理

Applicative Functorの拡張版。要するにFunctorってざっくり理解。

できること

「なんらかの文脈付きa」 と 「純粋なaから文脈付きbを作る関数」をうけとって、「文脈つきb」を作り出す 。

m a -> (a -> m b) -> m b -- m Monad

(a -> m b)という関数が先にあって、(m a)をこの関数に適応させたい時に使える。

type Monad :: (* -> *) -> Constraint
class Applicative m => Monad m where
  (>>=) :: m a -> (a -> m b) -> m b
  (>>) :: m a -> m b -> m b
  return :: a -> m a

できること に書いた変換は (>>=) で行える。


instance Monad Maybe where
    return         = Just
    fail           = Nothing -- これはシステムからCallされる。
    Nothing  >>= f = Nothing
    (Just x) >>= f = f x

Maybe は Monad。できること の動きが可能となっている。

ghci> Just 9 >>= \x -> return (x + 10)
Just 19
ghci> Nothing >>=  \x -> return (x + 10)
Nothing

MonadはFunctorなので、fmap も <*> もMonad関数でできる。

-- f は Functor , m は Monad
fmap :: (a -> b) -> f a -> f b -- functorの関数
(<*>) :: f (a -> b) -> f a -> f b -- applicative functorの関数
(>>=) :: m a -> (a -> m b) -> m b -- monad の関数

-- Functor の fmap は MonadのLiftMで同じことができる
ghci> liftM (+2) (Just 8)
Just 10
ghci> fmap (+2) (Just 8)
Just 10

-- Applicative の <*> は Monadのapで同じことができる
ghci> Just (+2) `ap` Just 8
Just 10
ghci> Just (+2) <*> Just 8
Just 10

何に役立つのか

以下のようなパターンマッチが、モナドの文脈(a -> m b)に処理を任せられるので、不要になる。

case ... of
  Nothing -> Nothing
  Just x  -> case ... of
               Nothing -> Nothing
               Just y  -> ...

たとえば + と - の演算ができる逆ポーランド記法計算機を普通に実装するとこんな感じ

-- input : "12+2-" ans 1
resolveRPN :: String -> Double
resolveRPN st = head $ foldl foldingFunc [] (words st)
    where   foldingFunc (x:y:ys) "+" = (x+y):ys
            foldingFunc (x:y:ys) "-" = (x-y):ys
            foldingFunc xs number = read number:xs

これに失敗するかも という文脈を与えたい

monadRPN :: String -> Maybe Double

畳み込みに使用する補助関数の出口をMaybeに変更

-- String -> Intへの変換をMaybeで扱う
readMaybe :: (Read a) => String -> Maybe a
readMaybe st = case reads st of [(x, "")] -> Just x
                                _ -> Nothing
-- 畳み込み関数にもMaybeを扱う
foldingFunc :: [Double] -> String -> Maybe [Double]
foldingFunc (x:y:ys) "+" = Just ((x+y):ys)
foldingFunc (x:y:ys) "-" = Just ((x-y):ys)
foldingFunc (x:y:ys) "*" = Just ((x-y):ys)
foldingFunc xs number = liftM (:xs) (readMaybe number)

失敗の文脈を与えたRPN

-- Maybe [Double] のリスト内が一つの結果になっているかを確認する
checkSize :: [Double] -> Maybe Double
checkSize [result] = return result
checkSize _ = Nothing

monadRPN :: String -> Maybe Double
monadRPN st = foldM foldingFunc [] (words st) >>= checkSize

monadRPN "1 2 * 4 +"  -- Just 6.0
monadRPN "1 2 * 4 + 5 *"  -- Just 30.0
monadRPN "1 2 * 4" -- Nothing
monadRPN "1 8 jifa"  --  Nothing

checkSizeはDo記法を使うと省略可能

monadRPN st = do
    [result] <- foldM foldingFunc [] (words st)
    return result

いまのMaybe値がNothingなのかJustなのか、と言った関心を持つ必要がないのが利点

補足
他、
リストもモナドだが、モノイドあたりから解説書きたいので別記事でやるかも(Maybe)

0
0
0

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
0
0