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モナドなどいくつか存在するが、それら「○○モナド」に共通する「モナド」って何ぞや?と内包的定義を考え出すから悩むのではと。りんごと電信柱、さて共通項は何でしょう?と聞かれて悩むような感じ。