= getLine() print(line) import System.IO (isEOF) main = loop where loop = do done <- isEOF if done then return () else do line <- getLine putStrLn line loop
-> IO z という形式の型を持つ関数 ◦ 特に、 main 関数 • IO をモナド変換子でラップしたモナドの中 ◦ a -> b -> … -> OtherMonadT Hoge (SomeMonadT Foo Bar IO) z などの形式 ◦ lift 関数をラップされた回数だけ使って IO を持ち上げる • MonadIO 型クラス ◦ MonadIO m => a -> b -> … -> m z という形式 ◦ liftIO 関数で IO を持ち上げる • フレームワークなどを使う場合は、これらを探す
の形式のもの ◦ putStrLn :: String -> IO () ◦ catchIOError :: IO a -> (IOError -> IO a) -> IO a ◦ return :: Monad m => a -> m a ◦ mapM :: Monad m => (a -> m b) -> [a] -> m [b] ◦ put :: Monad m => s -> StateT s m () • 操作ではないもの ◦ unsafePerformIO :: IO a -> a ◦ runState :: State s a -> s -> (a, s)
• <- は操作を実行した結果を取り出して束縛 • let は右辺をそのまま束縛 ◦ 操作ではないが do ブロックに書ける main = do let x = 1 + 2 :: Int y <- return (3 + 4) :: IO Int print (x + y :: Int) :: IO ()
x と同じ getProgName >>= putStrLn putStrLn =<< getProgName -- Applicative -- x <- getProgName; y <- getProgname; let z = (x, y) と同じ z <- (,) <$> getProgName <*> getProgName print z
(AppStat n) = do x <- getLine return (x, AppStat $ n + 1) appPutStrLn (AppStat n) l = do putStrLn $ show n ++ ": " ++ l return $ AppStat n main = loop $ AppStat 0 where loop s = do (line, s') <- appGetLine s s'' <- appPutStrLn s' line let AppStat n = s'' if n < 3 then loop s'' else return ()
m の操作は lift を経由して使う ◦ lift :: (MonadTrans t, Monad m) => m a -> t m a • 一般的に、 runHogehogeT という命名でモナド m に戻す関数が存在 main = runHogehogeT $ do opHogehoge1 lift $ print “This is I/O action” opHogehoge2 ここには HogehogeT IO の操作を書ける 全体としては IO 型 HogehogeT IO という型名はコード に現れてないことに注意
-> m (a, s)) • runStateT に状態の初期値(s型)と共に与えて剥がす • 追加される操作 ◦ get :: Monad m => StateT s m s ◦ put :: Monad m => s -> StateT s m () • 他によく使う操作 ◦ modify' :: Monad m => (s -> s) -> StateT s m () 関数を適用して状態を直接変更する ’ がついていると正格 ◦ gets :: Monad m => (s -> a) -> StateT s m a 状態を取り出し、関数で変換してから返す 関数としてフィールド名を指定すると便利
runAppStat :: Int } deriving (Eq, Show) appGetLine = do x <- lift getLine modify $ AppStat . (+1) . runAppStat return x appPutStrLn l = do n <- gets runAppStat lift $ putStrLn $ show n ++ ": " ++ l main = (`runStateT` AppStat 0) $ loop where loop = do line <- appGetLine appPutStrLn line n <- gets runAppStat if n < 3 then loop else return () 状態を受け渡すコードを隠蔽してくれる。 状態を取り出す gets や、状態を更新する modify という操作が追加される。
-> m a) • runReaderTに環境の値(r型)と共に渡して剥がす • 追加される操作 ◦ ask :: Monad m => ReaderT r m r ◦ local :: (r -> r) -> ReaderT r m a -> ReaderT r m a • よく使う操作 ◦ asks :: Monad m => (r -> a) -> ReaderT r m a 状態を取り出し、関数で変換してから返す 関数としてフィールド名を指定すると便利 • IO, IORef と使うと読み込みだけじゃなく状態も表現可
= AppStat { runAppStat :: IORef Int } deriving (Eq) appGetLine = do x <- lift getLine modify' (+ 1) return x appPutStrLn l = do n <- get' lift $ putStrLn $ show n ++ ": " ++ l get' = do r <- asks runAppStat lift $ readIORef r modify' f = do r <- asks runAppStat lift $ atomicModifyIORef' r (\x -> (f x, ())) main = do r <- newIORef 0 (`runReaderT` AppStat r) $ loop where loop = do line <- appGetLine appPutStrLn line n <- get' if n < 3 then loop else return () 設定を受け渡すコードを隠蔽してくれる。 設定を取り出す asks が追加される。 更新操作はないが、 IOモナドが配下にいるとミュー タブルに更新できる。
base パッケージの Control.Monad.IO.Class モジュール ◦ liftIO :: IO a -> m a いつでも1回の利用で IO を持ち上げられる ◦ MonadIO をモナド変換子で持ち上げたものも MonadIO となるよう実装 • 例1: instance (MonadIO m) => MonadIO (StateT s m) • 例2: instance (MonadIO m) => MonadIO (ReaderT r m)
m) MonadIO m => MonadIO (Pipe l i o u m) (conduitパッケージ) • MonadIO m => MonadIO (Proxy a' a b' b m) (pipes パッケージ) • MonadIO m => MonadIO (WebStateT conn sess st m) (spock パッケージ)
• https://haskelliseasy.readthedocs.io/en/latest/ ◦ Haskell Programming from First Principles の著者 • http://dev.stephendiehl.com/hask/ ◦ What I Wish I Knew When Learning Haskell ◦ ライブラリに限らず、情報量が多い • http://lotz84.github.io/haskellbyexample/ ◦ Go by Example の Haskell版
conn sess st newtype SpockCtxT ctx m a = SpockCtxT { runSpockT :: W.SpockAllT m (ReaderT (LiftHooked ctx m) m) a } deriving (Monad, Functor, Applicative, MonadIO) data PoolOrConn a where PCPool :: Pool a -> PoolOrConn a PCConn :: ConnBuilder a -> PoolOrConn a PCNoDatabase :: PoolOrConn ()