ISID テックブログ

ISIDが運営する技術ブログ

Common LispでREST APIを作ってみよう

ISID Xイノベーション本部の山下です。 このポストは電通国際情報サービス Advent Calendar 2021の11日目のポストです。

中途入社したばかりで右も左も分からないのですが、楽しそうな企画ですので参加させていただくことにしました! 今回は自分が普段よく使っているプログラミング言語Common Lisp」の紹介記事を書かせていただきます。

はじめに

皆さん、Common Lispというプログラミング言語をご存じでしょうか? 「括弧が多い」と有名なLispの方言の一つです。 情報系の学部などでは習ったことがあるかたもいらっしゃるかもしれません。

何はともあれ、Common Lispで書かれたHello, Worldを見てみましょう。

(format t "Hello, World~%")

ちょっと独特な感じの書き方になっていますね。 Common LispLisp系の言語では、S式と呼ばれるリストを使ってプログラムを記述します。
関数呼び出しはもちろん、すべての構文がこのS式と呼ばれるリストで記述されます。 なお、Common Lispでは、; 以降はコメントになります。

;; これはコメントです。`;` 以降の文字はすべてコメントになります
;; 関数呼び出し
(関数名 引数1 引数2 引数3)

;; if式
;; 最初の式 (equal var1 0)の評価結果が真であるなら
;; (format t "var1 is zero")が実行されて
;; そうでないなら
;; (format t "var1 is not zero")が実行されます
(if (equal var1 0) (format t "var1 is zero") (format t "var1 is not zero"))

関数呼び出しやifなどの特殊な構文(special formと呼びます)でも基本的に同じ形のS式になっています。

;; Common Lispでは基本的に以下のような式だけでプログラムが記述できます
(関数名か構文を示す文字 引数1 引数2 .... )

最初はとっつきづらいかもしれません。しかしS式の書き方は1種類しかないので些末な文法で迷うことなくなります。 このため慣れるとロジックに注力してプログラムを書けるようになってきます。

またLispで本当に実用的なプログラムが動いているのを見たことがないという声もたまに伺います。 そこで、今回はこのCommon Lispを使って実用的なプログラムの例として、REST APIを書く例をご紹介しようかと思います。

実用的なプログラムって書けるの?

では、早速ですが、REST APIサーバをCommon Lispで記述してみましょう。
ゼロから全部実装するのは大変なので、他の言語と同じようにライブラリを使って実装してみます。

(ql:quickload '(:ningle :clack))

quickloadという、Pythonのpip、JavaMavenのようなライブラリマネージャを利用してWebサーバ関連のライブラリを読み込みます。

;; サーバの実体をグローバル変数として宣言、作成する
(defvar *app* (make-instance 'ningle:<app>))

;;
;; "/"にGETアクセスすると、Hello, World!が返ってくるAPI
;;
;; - setfは代入を実行する構文です
;; - (ningle:route *app* "/" :method :GET) はwebフレームワークのningleの提供する
;;   アクセスがあった際に呼ばれるコールバック関数です
;; - lambda式は無名関数を宣言する構文です
;; 以下の式は、setf式を使って、"/"にGETアクセスがあった際に呼ばれるコールバック関数を、
;; lambda式を用いて宣言した無名関数で上書き代入するということになります。
(setf (ningle:route *app* "/" :method :GET) 
      
      #'(lambda (params)
          "Hello, World!"))

;;
;; "/hello"にPOSTアクセスした際のAPI実装
;; このAPIは、usernameというパラメータを与えて実行すると
;; "hello, 与えたusername" と返し
;; パラメータがない場合には、
;; hello, somebody と返すような内容となっています
;;

;; 以下の式は、上で作成した"/"に対するAPIと同様に、"/hello"に対するPOSTアクセスが行われた際の
;; コールバック関数の上書きを行っています。
;; ここでは、POST時の引数の処理を行うために、無名関数の宣言(lambda式)の中で
;; let式という変数を作成する構文を利用しています。
;;
(setf (ningle:route *app* "/hello" :method :POST)
      #'(lambda (params)
          ;; let式を用いて、usernameという変数を宣言し、
          ;; POST時のパラメータ(username)の値で初期化しています。
          (let ((username (cdr (assoc "username" params :test #'string=))))
            (if (not (null username))
              (format nil "Hello, ~A" username)
              "Hello, somebody."))))

(clack:clackup *app*)

これで完成です! 簡単ですね。

では早速アクセスしてみます。 まずはGETで"/"(ルート)にアクセスしてみます。

$ curl -s http://localhost:5000/
Hello, World!

ちゃんとHello, Worldと返ってきましたね。 今度はPOSTで"/hello"にアクセスしてみましょう。

$ curl -s -XPOST -d "username=Yamashita" localhost:5000/hello
Hello, Yamashita

$ curl -s -XPOST localhost:5000/hello
Hello, somebody.

ちゃんと与えたパラメータを元に応答を返してくれました! REST APIとして正しく動作してそうですね。

IDEなどについて

Emacsで開発するのが一般的なのですが、VisualStudioCodeのプラグインなども開発が進んでいるようです。 手元で試した限りだと小規模な開発では十分実用になりそうです。 https://marketplace.visualstudio.com/items?itemName=ailisp.commonlisp-vscode

Emacsを利用する場合はSLIMEという定番のIDE環境があります。
Emacsキーバインドが苦手でないかたはこちらをお勧めします。 https://common-lisp.net/project/slime/

まとめ

今回はCommon Lispの簡単な紹介と、ライブラリを利用してREST APIサーバの簡単な実装例を紹介してみました。
どうでしょうか、とっつきづらそうなCommon Lispですが、簡単な読み方を覚えてしまえばそんなに括弧が気にならなくなったのではないでしょうか? また、REST APIなども実装できる十分に実用的なプログラミング言語であるということも紹介できたのではないかと思います。

この記事で、Common Lispのことを括弧が多いだけの言語じゃなく、身近な問題を解決できる普通のプログラミング言語だと思っていただければ幸いです。

お勧めの参考書など

最後に、Common Lispを学習するためのお勧めの書籍を紹介して終わろうかと思います。

Land of Lisp

image

Common Lispの入門から始まった一通りの使い方が学べる内容となっています。 挿絵のLisp Alienがかわいいですね。

実践Common Lisp

image

タイトルの通り実践的な話に関して一通り書かれている書籍になります。 例えば、ファイルの入出力といった実際のプログラムを書く上では必要となるような情報がまとまっています。

このあたりの書籍がCommon Lispの入門にはお勧めです。

執筆:@yamashita.tsuyoshiShodoで執筆されました