Haskell

Haskellによる並列・並行プログラミング(12.3) チャットサーバを写経 & network-3.0対応

Haskellの並列処理モデルの勉強がてらに「Haskellによる並列・並行プログラミング」の12章にあるチャットサーバを写経した

そのとき、Network ライブラリでdeprecatedのwarningが出ていたのでその辺修正した記録

後日別記事でソースの内容解説についても書きたいな、と思っています

source codeは こちら

本に乗っていた方source codeは こちら

Network ライブラリ周り、他、本に直接乗っていなかった tell kick 関数はせっかくなので自作したため

オリジナルとコードが異なります(キモとなるトランザクション範囲は一緒になっているはずです)

当方Haskell初心者のため不手際な点もあると思いますが、やんわり指摘いただけると助かります


Networkライブラリのmigrate

さて、今回写経するにあたって対応したNetworkライブラリのmigrateについて話します

ちなみに、localで動かしていた限りでは非推奨の警告が出るのみで、本のソースコードそのままでも動きます

networkライブラリ 3.0での変更点は github にかかれています

その中で今回非推奨の警告が出る原因となっていたのは


Removing Network


たぶんこちらです

本のソースコードでは Network トップレベルに定義されていた listenOn を使っていたので

使えなくなった以上代替え手段で対応するしましょう

対応手段としてはNetwork.Socket.Socket data typeをそのまま扱う形に変更してもいいのですが

今回は別の手段でSocket -> Handleへ変換したほうが差分も少なくなるので良いと判断しました


socketToHandle :: Socket -> IOMode -> IO Handle

socketToHandle メソッドにSocketを渡すことでそのSocketをwrapしたHandleを取得できます

一緒にIOModeを渡す必要があるのですが、Socketは双方向の通信(read&write)を行うので ReadWriteMode使用します

handle <- socketToHandle csock ReadWriteMode

SocketをHandle化する利点としては


  • HandleにすることでFileと同じinterfaceで扱える

  • 今回のsocketから流れてくるデータが長さ固定ではなく、改行コードで区切られた文字列を一つのメッセージとして扱う要件のため hGetLine 関数を使用することでハンドリングが楽になる

  • 取得する文字列の型が Network.Socket.ByteString ではなく [Char] になるので馴染みのあるListの関数が使える

あたりでしょうか


socketの生成

地味にNetworkライブラリの更新でsocketの生成方法も変わっています

resolve port = do

let hints = defaultHints { addrFlags = [AI_PASSIVE]
, addrSocketType = Stream }
addr:_ <- getAddrInfo (Just hints) Nothing (Just port)
return addr
open addr = do
sock <- socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr)
setSocketOption sock ReuseAddr 1
let fd = fdSocket sock
setCloseOnExecIfNeeded fd
bind sock (addrAddress addr)
listen sock 10
return sock

Adress typeが間に噛むので一見すると少し複雑ですが、細かい設定値を設定することができるので有識者はこちらのほうがよいのでしょうか?


ちなみに、ドキュメントからのコピペです


主な変更点はこの二点となります

Socket周りを簡易的に扱える listenOn 関数がなくなってしまった?ので低レイヤの設定等が露出するようになってしまいましたが

望むのであればこれに対するwrapper関数を自作で書いてあげるとよいかもしれませんね、ただ実務では他言語同様なんらかのServerフレームワークを使用するのがよいでしょう(あるよね?)