Haskell本の感想とMonad

教養のために「ふつうのHaskellプログラミング」を読みました。Haskellを触ったことがない私のような人が読むのにちょうど良い本でした。

ただ、Monad則については読んでも良くわかりませんでした。Maybeモナド等、Monadインスタンスの使い方はわかるのですが、Monad則がどのようにして出てきて、どんな利点があるのか等の理論的な部分はページ量の関係か本に詳しく書いてありません。

そこで、Monadについて解説しているWebサイトを探して読んでみましたが、いまいち理解できてません。

ただ、IO処理等の副作用を持つ操作にMonadが利用できる理由については、以下のサイトを読むとなんとなくわかった気になれます。

Haskellだってバグるよねっていう話し(C言語の関数をHaskellで呼ぶ)

C言語の関数をHaskellから呼び出すための記述に関する仕様が存在します*1
自分が使っているHaskell処理系のGHCは、この仕様に準拠しています。そこで、この機能を使ってバグったC関数をHaskellから呼ぶことでメモリアクセス違反させてみます*2

// foo.c
#include "HsFFI.h"

static int mem;

HsPtr wrong_ptr(int wrong)
{
  if(wrong){
     return (HsPtr)0xcfcfcfcf;
  }else{
     return (HsPtr)&mem;
  }

}

このwrong_ptr関数を呼ぶHaskellコードは以下、

-- main.hs

import Foreign

foreign import ccall "wrong_ptr" wrong_ptr :: Int -> IO (Ptr Int32)

main = do
  ptr <- wrong_ptr 0            -- 変数memのアドレスがptrを束縛する
  pokeElemOff ptr 0 5           -- ptr[0] = 5
  peekElemOff ptr 0 >>= print   -- 5を出力
  
  ptr <- wrong_ptr 1            -- 0xcfcfcfcfがptrを束縛する
  pokeElemOff ptr 0 100         -- メモリアクセス違反!!!
  peekElemOff ptr 0 >>= print

で、実際にやってみると、*3

> ghc foo.c main.hs -ffi -o foo
> foo
5
          <------ここで異常終了
>

この例はわざとらしいですが、Haskellのプログラムも、わかりにくいタイプのバグが入り込む余地があるっていうことがわかると思います。
C言語の関数を使わなきゃ良いっていう意見もあると思いますが、win32apiやらLinux等のシステムコールを直に扱いたい時には使わざる得ないでしょう*4
また、絶対にC言語の関数を使わないっていうスタンスは、既存のライブラリを使ったほうが楽なのに、わざわざHaskellで書き直さなきゃいけないといった問題が発生する点でコストが高いと思います。


個人的には、デバッグ周辺のツールがもう少し充実しないと、普通のプログラムを書くのにHaskellを使う気になれません。上の例よりもっと複雑なコードがバグった時のデバッグ作業を考えると恐ろしすぎます。
GHC6.7からはデバッガが付属するそうなので、正式にリリースされたら使ってみようと思います。

*1:http://www.cse.unsw.edu.au/~chak/haskell/ffi/ffi.pdf

*2:このコードを書く際には、こちらの日記を参考にさせていただきました。http://d.hatena.ne.jp/E_Mattsan/20070616

*3:WindowsmsiパッケージからGHCをインストールした場合、cc1が存在しないっていわれますが、C:/ghc/ghc-6.6.1/gcc-libにPATHを通せば動きます(GHC6.6.1をデフォルトのパスにインストールした場合)

*4:win32apiは、ある程度Haskellの標準ライブラリとして用意されてるようです