GHCライブラリ
Haskellの標準ライブラリを少し眺めていたので、メモ。GHC6.6がリリースされてるけど、6.4.2しか調べてない。SRFIと違って、名前から機能が類推しやすくてよい。
http://www.haskell.org/ghc/docs/6.4.2/html/libraries/index.html
http://www.sampou.org/haskell/report-revised-j/io.html
ファイルやディレクトリを作ったり、消したり、存在するか確認するには、System.Directoryに色々
import System.IO cat fname = (openFile fname ReadMode)>>=(\h -> (cat_main h)>>(hClose h)) where cat_main h = (hIsEOF h) >>=(\eof -> if eof then (return ()) else (hGetLine h)>>=putStrLn>>(cat_main h))
openできなかったときは、エラー処理〜とか考えなくていいのは便利だね
SocketとかCGIとかあるらしい。Socketは、普通のSocketにMonadを被せて使いにくくしたもの。ここのSchemeコードをHaskellで書き直しただけの適当なSMTPクライアント
import Network.Socket import Network.BSD --send_mail 25 "202.xxx.yyy.zzz" "from@xxx.co.jp" "to@yyy.ne.jp" "hello!" send_mail port host mail_from rcpt_to message = do{s<-(socket AF_INET Stream 0); addr <- (inet_addr host)>>=(\h -> return (SockAddrInet port h)); (connect s addr) >>(check_reply_code s 220) >>(getHostName>>=(\h -> (send_command s ("HELO "++h) 250))) >>(send_command s ("MAIL FROM: <"++ mail_from ++ ">") 250) >>(send_command s ("RCPT TO: <" ++ rcpt_to ++ ">") 250) >>(send_command s "DATA" 354) >>(main_loop s (lines message)) >>(send_command s "." 250) >>(send_command s "QUIT" 221) >>(sClose s)} where send_command sock command suc_code = (send sock (command++"\r\n"))>>(check_reply_code sock suc_code) check_reply_code sock code = (recv sock 512) >>=(\s -> if (take 3 s)==(show code) then return () else (sClose sock)>>(fail ("smtp error :" ++ s))) main_loop sock messages = case messages of [] -> return () x:xs | (x==".") -> (send sock "..\r\n")>>(main_loop sock xs) | otherwise -> (send sock (x++"\r\n"))>>(main_loop sock xs)
手続き型言語で書くのと殆ど変わらないな。ローカル関数が使えるので、その分見やすくはあるかもしれない。何故かgetHostByNameが上手く動かない。それから、ある程度の規模になると、failした時の後始末が、あちこちに分散して気持ち悪いかも。dynamic-windがほしい。Haskellで書けるんだと思うけど。そのうち、簡単なサーバーでも書いてみよう。
Haskellはデバッグ大変らしい。てか、Buchbergerアルゴリズム書いたときに大変だった。monadのせいで"printfデバッグ"すら出来ないし(出来ないことはないかもしれないけど、面倒くさい)。一応、デバッグ用のTraceと、テストを自動で生成するQuickCheckなるものが。そのうち、調べる。QuickCheckは、このへんの例が分かりやすい
何に使うのか知らないけど、みんなprintf好きね
import Text.Printf main = printf "Today:%d/%d\n" (10::Int) (15::Int)
整数値の型は、デフォルトでは、Integerになるので、明示的にIntを指定しないといけない。不定数引数関数をどうやって実現してるのか、謎だ。気が向いたら、ソース読む。
putStrLn ("Today:"++(show 10)++"/"++(show 15))
より短くはある
CからHaskell呼ぶのも、HaskellからC呼ぶのもできるらしい。C以外に、JavaとかPerlとかできるらしい。あと、Windowsで、DLL呼べる。
どんなにライブラリが貧弱でも、 DLLさえ呼べれば なんとかなるよ! ハ_ハ ('(゚∀゚∩ なるよ! ヽ 〈 ヽヽ_)
とりあえず、簡単なMessageBox出す例と、ゴミ箱空にする例。
{-# OPTIONS -ffi #-} import Foreign import Foreign.C.String import Foreign.C.Types import Foreign.Ptr import System.Win32.DLL type HWND = Ptr Int32 foreign import stdcall "dynamic" ptr_to_recycle_t :: FunPtr Int32 -> Ptr Int32 -> CString -> Int32 -> IO Int32 foreign import stdcall "dynamic" ptr_to_msgbox_t :: FunPtr Int32 -> HWND -> CString -> CString -> CUInt -> IO CInt recycle = (loadLibrary "shell32") >>=(\hmod -> (getProcAddress hmod "SHEmptyRecycleBinA") >>=(return . ptr_to_recycle_t . castPtrToFunPtr) >>=(\f -> (newCString "")>>=(\s -> (f nullPtr s 0))) >>(freeLibrary hmod)) msgbox = (loadLibrary "user32") >>=(\hmod -> (getProcAddress hmod "MessageBoxA") >>=(return . ptr_to_msgbox_t . castPtrToFunPtr) >>=(\f -> (newCString "the World!") >>=(\s -> (newCString "MudaMudaMuda") >>=(\capt -> (f nullPtr s capt 0)))) >>(freeLibrary hmod))
一行目は、FFIを使うときに付けるお呪いらしい。"foreign import stdcall ..."はよく分からないけど、getProcAddressが返すのは、関数のアドレス(IO Addr)なので、Addr型を適切な型に変換するのに使う。stdcallは勿論呼び出し規約で、WINAPIなのでstdcall。Haskellのデータは軒並み、直接外部関数に渡すことはできないので、適切に変換する必要がある。整数とかはGHCが面倒見てくれるみたいだけど、文字列はダメっぽい
しかし、これは、一旦(\hmod -> ...)に入ったら、終わるまで、このスコープ抜けれない。そして、この外では、基本的に、DLL関数も呼べない。大体、DLLをロードするのは、プログラムの最初の方で、開放するのは、終わりのほうだから、長いプログラムになってくると、(\hmod -> ...)内に全部書かないといけない。それは辛すぎる。それはもう、スタート地点に土管があって入ったら、凄い長い工程を経て、やっと外に出たらゴール前で、上のコインとり損ねたやんーって話ですよ(意味不明)
まあ、そのためのunsafePerformIOなのかもしれないけど、そんなの使うくらいなら、Haskell使ってないよって話で、ここらへんいい解決策ないのか。そういや、昔なんかで、仮想関数テーブルから、アドレス経由でメンバー関数呼ぼうと思ったら無理ということがあったけど、Haskellならできるな
Graphics.HGLは、Windowsでは動かないとか、6.6では、Windows versionからは、HGLが削除されるとか。ぬー。Graphics.Win32.Windowや、Graphics.Rendering.OpenGLはWindowsでも使える模様。まあ、OpenGL使えればいいや。あと、GHC標準ではないけど、wxHaskellとか。そのうち、このへん使って、何か書いてみよう
Control.Concurrent。普通にスレッド作れるらしい。STMとかは、そのうち調べる
全く考慮されてないように思います。
main = putStrLn "こんにちわ"
は、即エラーで弾かれます(EUC-JPで保存すると、一応通るようだけど)
三角関数とか基本的な数値計算関数は、Floatingクラスにある(GHC.Float)。勝手にimportされるっぽい
乱数関数は、System.Random
Data.xxxに、ハッシュテーブルとか、Setとか色々なデータ構造。
Parsec。これがなかったら、Haskell使ってないかもしれないパーサジェネレータ
ネタ以外に使い道があるのか不明なTemplate Haskell(Language.Haskell.TH)
何に使えるのかすら分からないArrow(Control.Arrow)