syd_sydの日記

2006-10-19ずっと放置

[]undefinedやerrorを呼んだ時のExceptionはErrorCall?

undefinedやerrorで落ちそうなときもcatchできる。

ここで使う Control.Exception.catch の型は:

Control.Exception.catch :: IO a
                           -> (GHC.IOBase.Exception -> IO a)
                           -> IO a

Exception型

http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Exception.html#t%3AException

ErrorCallが投げられるっぽい。実際:

ghci> Control.Exception.catch (seq (error "error!!!") $ return ()) (\(Control.Exception.ErrorCall e)->putStrLn ("exception:"++show e))
exception:error!!!
ghci> Control.Exception.catch (seq undefined $ return ()) (\(Control.Exception.ErrorCall e)->putStrLn ("exception:"++e))
exception:Prelude.undefined
ghci> Control.Exception.catch (seq (head []) $ return ()) (\(Control.Exception.ErrorCall e)->putStrLn ("exception:"++e))
exception:Prelude.head: empty list

意図しないランタイムエラーで死んでしまう前にこの保険!?

2006-06-14

昨日のHAppSに引き続き、Haskellでの「正しいネットワークプログラムの書き方と良いライブラリ」を知るためにソースを読んでいます。

(Network.SocketとかはStringを使っている時点でアウト)

[]Data.ByteString

lambdaBotソースを読んでいたら、ネットワーク送信部分らしき

import qualified Data.ByteString.Char8 as P (pack, hPut, hPutStrLn)
:
:
            Just msg -> P.hPut h $ P.pack $ IRC.encodeMessage msg "\r"

という記述を発見 (IRC.hs, 250行目付近)。

hPut? Data.ByteString? … あった。 ?? しかし私のghc-6.4.2には入ってない。 ghcのトップから辿れるドキュメントには載ってない。GHC6.5からは入るのかな?GHC6.6〜らしい。

Haskell Communities and Activities Report

ついこの間出たらしい。4.6.3を読むと,ByteStringは http://www.cse.unsw.edu.au/~dons/fps.htmlが元になっていると。 newBinaryやSerTHを漁っていたので面食らいました。でもserializingとはまた別の話かもしれない?

とりあえずネットワーク周りはこれを使っておけば良さそう。

クラスの話

に、屈指のHaskellハッカーな人からコメントを貰いました。多謝。

http://d.hatena.ne.jp/syd_syd/20060614

[]SerTHとData.ByteString

どちらもIOにはhPutBuf等が使われている。高速な文字列操作にはByteStringを、様々な型を簡単にserializationしたい時にはSerTHを使うのが良さそう。

SerTHは、Template Haskellによりserializeのためのインスタンス宣言(メソッドの定義)を簡略化することができる。

2006-06-13

今のところ出せるネタも無いので(鋭意作成中)、ひとくちメモを書いていきます。GHCライブラリドキュメント丸写しです。果たして需要はあるのか?

フィボナッチとか高階関数とか継続渡しとか型クラスとか色々は置いといて、Haskellを使って信頼性の高い、実行効率の良いアプリを書くには?を観点に書いてい(けたらいいなあ)。。。

ふつケル持ってないのでかぶっていたらごめんなさい。

今回は例外についてですが、純粋関数的な(IO以外の)例外的状況には Maybeモナドやリストモナドを使いましょう。ErrorTというモナド変換子もあるらしい(使ったことない)。「モナドのすべて」参照。

[][] evaluate と return : 項の強制評価

Control.Exception.evaluate

evaluate :: a -> IO a

returnと似て非なるもの。渡された項を評価する。これが効いてくるのは、「無限に続く(止まらない) 計算」や「エラーを投げる計算」を食わせた時。

例:

do
  return $ 1 `div` 0 -- 0除算
  putStrLn "I'm still alive!"

I'm still alive!

となるのに対し*1

do
  Control.Exception.evaluate $ 1 `div` 0 -- 0除算
  putStrLn "I'm still alive!"

は 1/0が評価されて、

*** Exception: divide by zero

となる。

必ず評価する、という点ではseq と似てるけど、seq は第一引数を捨てるし、 evaluate は IOモナドを返す点で違う(本文参照)。 次のthrowとthrowIOの違いに似てる?

[] throw

Control.Exception.throw,Control.Exception.throwIO

Haskellの例外はIOモナド内でしかcatchすることができませんが、投げるのはどこでもできます:

throwIO :: Exception -> IO a 
throw :: Exception -> a 

throwIOはIOモナドの中で、throwはどこでも。

ただし throwIO ex `seq` a は例外をraiseしない (マニュアルより)。 ややこしいですが、前述の evaluate同様、 IO a 型の値はIOモナドの中でしか効果がないです。このような場合はunsafePerformIOを使うか、素直にthrow exとやりましょう。

[] catch

Control.Exception.catch

catch 
  :: IO a -- 例外をraiseするかもしれないアクション
  -> (Exception -> IO a) -- 例外ハンドラ
  -> IO a 

省略。

引数を入れ替えただけのhandleや、catchJust, handleJustなども。

[] bracket

Control.Exception.bracket

bracket 
  :: IO a -- 最初に行う (e.g.リソースを取得して返す) アクション
  -> (a -> IO b) -- 最後に行う (リソースの開放を行う) アクション
  -> (a -> IO c) -- 間に行う (メインの)  アクション
  -> IO c 

メインアクションの中で例外がraiseされた場合に、必ずリソースを開放してから例外がre-raiseされます。

高レベル言語には必ず存在する try-finally はこれでやりましょう。

特にリソースを意識しない場合、文字通りfinallyなんかもあります。

本文にはopenFileと組み合わせる例が載っています。この例では必ずファイルが閉じられます。

[][][] bracketの例 : HAppSより

404 Not Found

を偶々読んでいて知りました。これは実装に標準のNetwork.Socketを使っているぽい。

http://happs.org/HAppS/src/HAppS/Protocols/SMTP.hs からの例:

hostSend :: HostName -> Int -> Envelope String -> IO ()
hostSend host portNum msg = bracket (connectTo host (PortNumber $ fromIntegral portNum))
                                    (\h -> hClose h `E.catch` (\_ -> return ()))
                                    (flip handleSend msg)

handleSend :: Handle -> Envelope String -> IO ()

connectToで接続したらhandleSendを実行する。bracketにより、handleSendが失敗するしないに関わらず、hCloseによりハンドルは正しく閉じられる。hCloseの例外処理は書いておく必要がある。 Javaのfinallyブロック内で例外が投げられうるのと同じ。


[] block と asynchronous exception

パス。

実行効率の測定(プロファイリング)

あとで書けたら書く。

[][]network-alt

network-alt

Networkとは別のライブラリらしい。後で調べる。

*1:return (1`div`0) >>= \_-> putStrLn "..." となり、遅延評価により除算されない。これに対し return (1`div`0) >>= \x-> putStrLn $ show x とやればputStrLnに必要なため1/0が評価され例外がraiseされる。

2006-06-12お初です

Haskellについての日記はてなで書いていました。

http://d.hatena.ne.jp/syd_syd/searchdiary?word=%2a%5bHaskell%5d

今後はこちらで書かせて頂こうと思います。どうぞ宜しくおねがいします。

(どっちかというと「Haskellで何かを書く」ことより「Haskellそのもの」に興味があるみたいです。)