函数型なんたらの集い2014でモナドについて話してきた


最近私的にモナドが非常に熱いのでそれについて話してきました。
しかし資料としては要改善点が多いですね...
図入れるとか具体的なコード入れるとか色々出来たのですけど。

具体例をスライドに入れられなかったため、trivialな例を示す。
おおまかな雰囲気は感じられると思う。

明記してないけど、純粋関数は任意のモナド内でモナドとは関係なく使えます。

-- Derivingに必要
{-# LANGUAGE GeneralizedNewtypeDeriving #-}

import Control.Applicative (Applicative)
import Control.Monad.IO.Class (liftIO, MonadIO)
import Control.Concurrent (threadDelay)


-- DSLとなる型 (IO a のnewtype)
newtype Hello a = Hello { unHello :: IO a }
    deriving (Functor, Applicative, Monad, MonadIO)

-- DSLインターフェース(DSL定義)
class Monad m => MonadHello m where
    getName :: m String
    echo :: String -> m ()
    prompt :: m ()
    wait :: m ()

-- HelloのDSL化
instance MonadHello Hello where
    getName = liftIO getLine
    echo = liftIO . putStrLn
    prompt = liftIO $ putStr "Enter your name: "
    wait = liftIO $ threadDelay 400000

-- HelloでSPECIALIZE
{-# SPECIALIZE getName :: Hello String #-}
{-# SPECIALIZE echo    :: String -> Hello () #-}
{-# SPECIALIZE prompt  :: Hello () #-}
{-# SPECIALIZE wait    :: Hello () #-}

-- run関数 (Hello DSLを実行し、IO上の効果とする)
runHello :: Hello a -> IO a
runHello = unHello


--------------------------------------------------------------------------------
-- DSLを用いたロジック

greet :: MonadHello m => m ()
greet = do
    echo "What's your name?"

    -- prompt表示
    prompt

    name <- getName

    -- ifやcaseは問題なく使える
    case name of
        ""      -> outputAndGreetAgain "Sorry, I can't hear you."
        "ruicc" -> outputAndGreetAgain "Hi, ruicc!"
        "quit"  -> 
            -- 実行終了
            echo $ "Good bye!!"
        _       -> outputAndGreetAgain $ "Hello, " ++ name ++ "!"

{-# SPECIALIZE greet :: Hello () #-}

outputAndGreetAgain :: MonadHello m => String -> m ()
outputAndGreetAgain str = do
    -- 表示
    echo str
    -- 待つ
    wait
    -- 再帰
    greet

{-# SPECIALIZE outputAndGreetAgain :: String -> Hello () #-}


--------------------------------------------------------------------------------
main :: IO ()
main = do
    putStrLn "This is a DSL sample."
    putStrLn "---------------------"

    -- DSLを実行 (DSLの多相型(MonadHello m)をHelloに固定する)
    runHello $ greet

懇親会

懇親会では@notogawaさんにAgdaについて教えてもらったり名古屋の型とワイワイしたりしてました。
Agdaは多分20-30分くらいしか話してないですけど独学3ヶ月分くらい進んだのではーと思いますねいや適当ですよ。
まあVimmerとしては一番の問題はEmacsだったりします。