Open5
Haskell学習メモ
モナドの動作確認用
-- Fanctor
-- (<$>) :: Functor f => (a -> b) -> f a -> f b
(*2) <$> (Just 3) -- Just 6
-- Applicative
-- (<*>) :: Applicative f => f (a -> b) -> f a -> f b
(return (*2)) <*> (Just 3) -- Just 6
-- Monad
-- (>>=) :: Monad m => m a -> (a -> m b) -> m b
(Just 3) >>= (\x -> return $ x * 2) -- Just 6
[1,2,3] >>= return . (2*) -- [2,4,6]
f :: (Monad m, Num b) => m b -> m b -> m b
f mx my = do
x <- mx
y <- my
return $ x + y
-- f (Just 1) (Just 2) -- Just 3
-- f [100,200,300] [1,2,3] -- [101,102,103,201,202,203,301,302,303]
関数モナド
式(f >>= g)
は、関数\x -> g(f(x), x) :: a -> a
を返す。
((+1) >>= (^)) :: Integral a => a -> a
-- 実行例
((+1) >>= (^)) 2 -- (2 + 1) ^ 2 = 9
以下は、上と同じコードをdo記法で書いたコード。
f :: Integral a => a -> a
f = do
a <- (+1)
b <- (a^)
return b
IOモナドは第一級オブジェクト
print
などの型IO a
を持つ式は、関数の引数に指定したり、リストに入れたタイミングでは副作用が発火しない。
- 例えば、C言語で
f (printf "hello")
と書くと、関数f
の実引数の式が評価されるタイミングで、"hello"
を出力するという副作用が発火する。 - 一方、Haskellでは、
f $ print "hello"
のように関数の引数にprint
の式を指定しても、print
式の戻り値が仮引数に束縛されるのみで、副作用は発火しない。
toFizzBuzz :: Int -> String
toFizzBuzz x
| mod x 15 == 0 = "FizzBuzz"
| mod x 5 == 0 = "Buzz"
| mod x 3 == 0 = "Fizz"
| otherwise = show x
execute :: [IO ()] -> IO ()
execute [] = return ()
execute (action:actions) = do
action
execute actions
fizzBuzzTest :: IO ()
fizzBuzzTest = do
-- mapのタイミングでは、printの副作用は発火しない
-- print の戻り値である IO () のリストが生成されるのみ
let actions = map (print . toFizzBuzz) [1 ..] :: [IO ()]
-- let actions = [1 ..] >>= (return . print . toFizzBuzz) :: [IO()]
-- printの副作用は、ここ↓で発火する
execute (take 20 actions) :: IO()
IORef
-- newIORef :: a -> IO (IORef a)
-- readIORef :: IORef a -> IO a
-- writeIORef :: IORef a -> a -> IO ()
-- modifyIORef :: IORef a -> (a -> a) -> IO ()
ioref = do
-- newIORef :: a -> IO (IORef a)
a <- newIORef 1
-- readIORef :: IORef a -> IO a
a' <- readIORef a
-- readIORef :: IORef a -> IO a
print =<< readIORef a
-- 以下のコードは、上記コードに型注釈を書いたもの。
-- a <- (newIORef 1 :: IO (IORef Int))
-- a' <- (readIORef (a :: IORef Int) :: IO Int)
-- (print =<< (readIORef (a :: IORef Int) :: IO Int)) :: IO ()
mioref :: IO()
mioref = do
a <- newIORef 1
print =<< readIORef a -- 1
modifyIORef a (\x -> x + 1)
print =<< readIORef a -- 2
モナドを説明することが難しい理由
モナドにもIOモナドやReaderモナドなどいくつか存在するが、それら「○○モナド」に共通する「モナド」って何ぞや?と内包的定義を考え出すから悩むのではと。りんごと電信柱、さて共通項は何でしょう?と聞かれて悩むような感じ。