HaHaHa!(old)

2006-09-08

HaskellモナドIO(つづき)

Action モジュール

module Action (Action, Handle    -- 型構成子のみエクスポート
              ,Mode(..)          -- 型構成子,データ構成子ともにエクスポート
              ,run,exec,eval,initialWorld
              ,openFile,hClose,hGetChar,hPutChar
              ,hIsEOF,hGetString,hPutString
              ) where

-- Action 駆動関数および合成関数 ---------------------------------------

newtype Action a = Act (World -> (a, World))

run  :: Action a -> World -> (a,World)
run (Act a) w = a w

exec :: Action a -> World -> World
exec = (snd .) . run

eval :: Action a -> World -> a
eval = (fst .) . run

unit  :: a -> Action a
unit x = Act (\ w -> (x,w))

(>=>) :: Action a -> (a -> Action b) -> Action b
(Act a) >=> f = Act (\ w -> let (x,w') = a w in run (f x) w')

(>->) :: Action a -> Action b -> Action b
a >-> b = a >=> const b

-- Monad インスタンス宣言 ----------------------------------------------

instance Monad Action where
  return = unit
  (>>=)  = (>=>)

-- 具体的なアクション --------------------------------------------------

data Mode = R | W | A            deriving Show

openFile :: Mode -> Action Handle
hClose   :: Handle -> Action ()

hGetChar :: Handle -> Action Char
hPutChar :: Handle -> Char -> Action ()

hIsEOF :: Handle -> Action Bool

hGetString :: Handle -> Action String
hGetString h = hIsEOF h >>= \ eof ->
               if eof then return ""
                      else hGetChar   h >>= \ c  -> 
                           hGetString h >>= \ cs -> 
                           return (c:cs)

hPutString :: Handle -> String -> Action ()
hPutString h ""     = return ()
hPutString h (c:cs) = hPutChar h c >> hPutString h cs

-- World の実装および World を参照する API の実装 ------------------

type World    = File
type File     = Handle
type Handle   = (Stat,Contents)
data Stat     = Closed | Opened Mode deriving Show
type Contents = ([Char],[Char])

openFile m = getWorld >>= \ f@(s,(c1,c2)) -> 
             case s of
             (Opened _) -> error "File is busy"
             _          -> let f'  = (Opened m, cs')
                               cs' = case m of
                                       R  -> (c1,c2)
                                       W  -> ([],[])
                                       A  -> (foldl (flip (:)) c1 c2, [])
                           in putWorld f' >> return f'

hClose _ = getWorld >>= \ f@(s,(c1,c2)) ->
           let f' = (Closed, ([],foldl (flip (:)) c2 c1))
           in putWorld f' >> return ()

hIsEOF _ = getWorld >>= \ f@(s,(c1,c2)) ->
           case s of
             Closed -> error "file is closed"
             _      -> return (null c2)

hGetChar _ = getWorld >>= \ f@(s,(c1,c2)) ->
             case s of
               Closed   -> error "file is closed"
               Opened W -> error "file is opened for writing"
               _ -> case c2 of
                      []   -> error "EOF is detected"
                      c:cs -> let f' = (s,(c:c1,cs))
                              in putWorld f' >> return c

hPutChar _ c = getWorld >>= \ f@(s,(c1,c2)) ->
               case s of
                 Closed   -> error "file is closed"
                 Opened R -> error "file is opened for reading"
                 _        -> let f' = (s,(c:c1,c2))
                             in putWorld f' >> return ()

-- World の直接参照と更新 ----------------------------------------------

getWorld :: Action World
getWorld = Act (\ w -> (w,w))

putWorld :: World -> Action ()
putWorld w = Act (\_ -> ((),w))

initialWorld :: World             -- 世界の始まり
initialWorld = (Closed, ([],[]))

世界は一つのファイルのみから構成されていて,Actionを使って,これにを読み書きする.世界の始まりには空のファイルがある.

アクションの実行をシミュレートする

アクションを用いたプログラムを以下のように用意する.

import Action
import Data.Char

mainAction :: Action ()
mainAction = do hw   <- openFile W                -- ファイルオープン
                hPutString hw "hello, haskell."   -- ファイルに書き出し
                hClose hw                         -- ファイルクローズ
                hr   <- openFile R                -- ファイルオープン
                str1 <- hGetString hr             -- ファイルから読み込み
                hClose hr                         -- ファイルクローズ
                ha   <- openFile A                -- ファイルオープン
                hPutString ha (capitalize ha)     -- ファイルに追加
                hClose ha                         -- ファイルクローズ

capitalize :: String -> String
capitalize = unwords . map cap . words
  where cap ""     = ""
        cap (c:cs) = toUpper c:cs

mainAction がプログラムエントリポイントである.これを実行してみよう.

*Main> run mainAction initialWorld
((),(Closed,("","hello, haskell.Hello, Haskell.")))

結果はタプルである,第一要素は内側から見たプログラムの値,すなわち ().

第二要素は,アクション実行後の世界の値,すなわち File の値(クローズ状態,

中身("","hello, haskell.Hello, Haskell."))である.

main :: IO ()

で,Haskell の main :: IO () と上の mainAction :: Action () が対応していると見るわけである.

mainAction :: Action () を「実行」するということは,

run mainAction initialWorld

評価することであった.つまり,mainAction だけを評価しても何も起こらないのである.この傳でいくと Haskell において,プログラムエントリポイントは main であるが,プログラムの「実行」は main :: IO () を評価することではないということになる.IOモナド評価しても何もおこらないのである.Haskellでは処理系が run にあたるものを main に適用し,それを評価することで「実行」されるということだろう.

  • IO * はアクションを表す値にすぎない.
  • IO * を評価しても入出力は「実行」されない.
  • return や (>>=) はアクションを生成したり,結合する糊として使える.
  • return や (>>=) が入出力の「実行」を司さどるわけではない.

というわけなんだね.

WanWan2012/12/20 19:35It's a joy to find smoenoe who can think like that

fzdhbikuvmgfzdhbikuvmg2012/12/21 14:27vRcISP <a href="http://fqnhzhjrizya.com/">fqnhzhjrizya</a>

sosqkjcsosqkjc2012/12/21 23:00ozNcCu , [url=http://ikiafqwobwbl.com/]ikiafqwobwbl[/url], [link=http://fgkwwwfcfgkc.com/]fgkwwwfcfgkc[/link], http://sqcfiwuebhng.com/

hlinqiiazvvhlinqiiazvv2012/12/23 08:25eaHUSF , [url=http://pwjwmdlfasjr.com/]pwjwmdlfasjr[/url], [link=http://yqmhlvjsgbhd.com/]yqmhlvjsgbhd[/link], http://fpxbkoptslbg.com/