人気ブログランキング | 話題のタグを見る

System.Time モジュールの使い方

Data.List モジュール探検の最中だが、ちょっと寄り道をして System.Time モジュールを散策してみる。Haskell の標準モジュールの概観は Haskell Hierarchical Libraries で眺めることができるが、かなりの量がある。

今日はその中から System.Time モジュールを使って、現在の日付と時刻を表示することに挑戦する。例によって、プログラムファイルを作らずに、ghci のコマンドラインからの操作だけでそれをやってみよう。ライブラリの使い方は、その中の関数がどう働くかをリアルタイムに見るのが一番よくわかるからだ。

まず、Prelude に System.Time モジュールをインポートする。
Prelude> :m System.Time
Prelude System.Time>


つぎに、現在の日付と時刻を getClockTime 関数で取得する。
Prelude System.Time> getClockTime
Loading package old-locale-1.0.0.2 ... linking ... done.
Loading package old-time-1.0.0.5 ... linking ... done.
Tue Aug 16 00:13:27 東京 (標準時) 2011


ghci では上のように表示されるが、getClockTime のタイプを見ると、戻り値は IO ClockTime 型なので、IO モナドとして扱わなければならない。
Prelude System.Time> :t getClockTime
getClockTime :: IO ClockTime

したがって、次のように show 関数の引数として getClockTime の戻り値を渡すことはできない。
Prelude System.Time> show getClockTime

:1:0:
No instance for (Show (IO ClockTime))
arising from a use of `show' at :1:0-16
Possible fix: add an instance declaration for (Show (IO ClockTime))
In the expression: show getClockTime
In the definition of `it': it = show getClockTime

こういうときは、return . show のように show 関数の戻り値を return で IO a 型に変換してやれば、return . show 関数が IO モナド型の関数になるので、getClockTime の戻り値を >>= 演算子でパイプしてやることができる。>>= 演算子は、両側の関数が IO モナド型の関数(1引数で戻り値がIO a型の関数)のとき、左の関数の戻り値 IO a から a だけを取り出して右側の関数に渡す働きをするからだ。
Prelude System.Time> getClockTime >>= return . show
"Tue Aug 16 00:19:13 \147\140\139\158 (\149W\143\128\142\158) 2011"

また、putStrLn 関数は、IO モナド型の関数だから、直接に getClockTime の戻り値を putStrLn . show 関数に注ぎ込むことができる。show 関数が必要なのは、getClockTime 関数の戻り値が IO ClockTime 型のため、show 関数で文字列に変換する必要があるからだ。
Prelude System.Time> getClockTime >>= putStrLn . show
Tue Aug 16 00:20:48 東京 (標準時) 2011


IO モナドは Haskell の鬼門だが、ghci で IO モナドを操作するときは、1引数で戻り値が IO a 型の関数>>= 演算子でつないで使うと割り切って考えるとよい。たとえば \x -> return ( read x :: Int) 、\x -> return ( x * x ) という関数はいずれも1引数 x をとり、IO a 型の戻り値を返すから IO モナドの関数だ。したがって、getLine と上の二つの関数を >>= 演算子でつないで使うと、コマンドラインから入力した整数を二乗して表示することができる。
Prelude System.Time> getLine >>= \x -> return (read x :: Int) >>= \x -> return (x * x)
12
144


これは do 命令で IO モナドを使うときと随分記述が異なるが、IOモナドの理解が進むにつれ、do 表記の場合も基本的には、1引数 IO a 型戻り値の関数を >>= でつないでいることが分かる。したがって、上の形で IO モナドの関数を操作することに慣れれば、do 表記のときにもプログラムの意味がよく分かるので、手続き型のプログラムからの類推でプログラムするときよりも落とし穴に陥りにくい。

System.Time 関数の多くがIOモナド型の関数だから、それらの関数を利用する際には、1引数で IO a 型の戻り値の関数をこしらえて >>= 演算子でつないでやればいい。たとえば、時間表示の前に Current time is という文字を入れたいときは次のようにする。
Prelude System.Time> getClockTime >>= putStrLn . (\x -> "Current time is " ++ show x)
Current time is Tue Aug 16 00:53:04 東京 (標準時) 2011


System.Time モジュールの探検のはずが IO モナドの説明になってしまったが、入出力関係のプログラムをいじるときには IO モナドは避けられない。しかし、IO モナドの世界とは、1引数で戻り値 IO a 型の関数を >>= 演算子でつないだものだと割り切ると、あとの工夫はどうやって1引数で戻り値 IO a 型の関数を作るかということになるので、必要以上にIO モナドを怖がらなくても済む。

この説明だけでは IO モナドが分かりにくいかもしれない。IO モナドについては、このブログで以前に詳しく検討した。記事は「Haskell 記事リスト」タグからたどることができる。
by tnomura9 | 2011-08-16 01:23 | Haskell | Comments(0)
<< Random モジュールの使い方 Data.List モジュール... >>