t-cool

Common Lispでsocketプログラミング(後編)

前回につづいて、Common Lispでsocketを扱うためのライブラリであるusocketの使い方について書かれた記事を紹介します。

前回の記事をまだお読みでない方は、本記事を読み進める前に、Common Lispでsocketプログラミング(前編)をお読みください。

今回の記事は、Smith Dhumbumroongさんの Socket programming in Common Lisp: how to use usocket’s with-* macrosという記事です。

今回は、前回のSidさんのコードを、with系マクロを使って書き直すと、どれだけコードが簡潔に、また安全になるかについて説明されています。では、読んでいきましょう。

Common Lispでsocketプログラミング:usocket with系マクロの使い方

最近、Common Lispでsocketプログラミングをしようと思い、(広く使われている)usocketライブラリを使うことにしました。

最初、socketライブラリのAPIの使い方に苦労しました。色々なチュートリアルや入門ガイドを読んで、with系マクロを使うことが推奨されていることを知りました。特に、Sid Heroorさんのこの投稿は参考になりました。特に、with-connected-socketマクロwith-client-socketマクロwith-server-socketマクロwith-socket-listenerマクロが推奨されていましたが、これらは、usocketライブラリを便利で明快に使えるように提供されているマクロです。これらのマクロは、エラーが適切に処理されたり、usocketを使い終わった後に、全てのsocketを適切に閉じることを保証してくれます。

問題は、これらのマクロを使うための入門ガイドやチュートリアルがなかったことでした。何時間もチュートリアルを探し回って無駄な時間を過ごしたあと、覚悟を決めて、これらのマクロのソースコードを読むことにしました。ソースコードを読んでみると、意外にもこれらのマクロは簡単に理解できるものでした。

基本的には、with-connected-socketマクロの上に、他のwith系マクロが作られています。with-connected-socketマクロの役割は、自動的にソケットを変数に束縛して、すべてのエラーと例外を処理するwith-mapped-conditionsマクロに本体(body)を渡して、処理から出るときに、そのsocketを破壊することを保証することです。with-client-socketマクロは、クライアントがサーバーにsocketで接続するのに便利なインターフェイスを提供してくれます。with-client-socketマクロは、自動でソケットを変数に束縛するだけでなく、クライアントにsocket streamも作ります。with-socket-listenerマクロは、socket-listen関数に対して、便利なインターフェイスを提供してくれて、結果として生じたsocketを、with-server-socketマクロに返します。with-server-socketマクロは、その返しとして、socket-listenが呼び出された結果作られたsocketを、with-connected-socketマクロに渡します。

前の記事でSidさんが書いた関数を、これらのwith系マクロで書き直すと、次のようになります。

サーバーのコード:

(defun start-simple-server (port)
  "Listening on a port for a message, and print the received message."
  (usocket:with-socket-listener (socket "127.0.0.1" port)
    (usocket:wait-for-input socket)
    (usocket:with-connected-socket (connection (usocket:socket-accept socket))
      (format t "~a~%" (read-line (usocket:socket-stream connection))))))

クライアントのコード:

(defun start-simple-client (port)
  "Connect to a server and send a message."
  (usocket:with-client-socket (socket stream "127.0.0.1" port)
    (format stream "Hello world!~%")
    (force-output stream)))

これらのマクロの組み合わせのおかげで、サーバーとクライアント両方のコード数を大幅に削減できることに注目してください。また、プログラムが、より安全に、また理解しやすくなります。これは、Common Lispのマクロの力を示す一例です。Pythonのような他の言語では。ここで見たようなwith系マクロに匹敵するものを導入しようとすれば、新しいバージョンが必要になることでしょう。一方、Lispでは、誰もが新しい制御構造を、ライブラリの一部として、導入できます。with系マクロの定義自体が、usocketで重要な関数を安全に使うためのガイドとして役割を果たしていることが気に入っています。

もう一度いいますが、Common Lispは、他のLisp方言と同様に、私に感銘を与えてくれます。これからもLispについてより多くのことを学ぶことが楽しみでなりません。