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フレームワークを使用するのがよいでしょう(あるよね?)