今回紹介するClojure/ClojureScriptはLISP系の言語の一種です。シンプルでありながら実用性に重きが置かれており、JVMや.NET、JavaScript上で動かすことができます。そのため、LISPの良さを活かしつつ様々な環境でアプリ開発ができます。今回は特にClojureをJavaScriptに変換して実行できるClojureScriptを使う方法を紹介します。

  • ClojureでWebアプリを作ってみたところ

    ClojureでWebアプリを作ってみたところ

ClojureとClojureScriptについて

もともと、ClojureはJavaの仮想マシンであるJVM上で動くLISP処理系として2007年に開発されました。Javaの様々なAPIをサポートするJVMは非常に高機能であり、JVMで動くClojureは最初から多くの実用的な機能を利用できる言語でした。そして、Java仮想マシンのバイトコードにコンパイルされるため実行速度も速いのが特徴です。また、コンパイル言語でありながら動的な汎用プログラミング言語であり、型推論を利用してJavaのAPIに手軽にアクセスできます。

  • ClojureのWebサイト

    ClojureのWebサイト

その後、実用的なLISP処理系として人気の出たClojureは、JVMだけでなく.NET FrameworkやJavaScript/Nod.jsにも移植されました。特に、JavaScript上で動くClojureをClojureScriptと呼びます。これはJavaScriptに変換されて実行されるため、コマンドラインやWebサーバーで動くNode.js上で動かすこともできますし、Webブラウザ上で動かすこともできます。つまり、本格的なWebアプリをClojureScriptだけで作ることができます。

ClojureScriptのインストール

ClojureをJavaScriptに変換する方法はいくつかあるのですが、Node.jsのライブラリlumoを使うのが手軽なので、ここではNode.jsをインストールし、npmでlumoをインストールする方法を紹介します。

最初にNode.jsをインストールしましょう。Node.jsはこちらのWebサイトからインストーラーをダウンロードできます。

  • 最初にNode.jsをインストールしよう

    最初にNode.jsをインストールしよう

なお、インストーラーは英語ですが右下にある[Next]ボタンを数回クリックすればインストールが完了します。気をつける点ですが、Windowsにインストールする場合には、npmが使えるようにインストールの際に「Add to PATH」を有効にした状態でインストールしましょう。

  • Node.jsのインストールの時「Add to PATH」を有効にした状態でインストールしよう

    Node.jsのインストールの時「Add to PATH」を有効にした状態でインストールしよう

次に、コマンドラインからnpmを利用してlumoをインストールします。Windowsでは「コマンドプロンプト」(または「PowerShell」)、macOSでは「ターミナル.app」を起動して、以下のコマンドを実行しましょう。

$ npm install -g lumo-cljs

対話型実行環境のREPLを試してみよう

lumoでは対話型のインターフェイスであるREPLを備えています。REPLを使うと一行ずつプログラムを実行できます。文法のテストができるので便利です。

$ lumo -r

試しにREPLに計算式を入力してみましょう。例えば、一般的なLISPで使えるS式である『(+ 1 (* 2 3))』と入力してみましょう。すると、1 + 2 * 3を計算して7と答えが表示されます。なお、Clojureで計算式を記述するには『(演算子 引数1 引数2)』のように記述するのが特徴です。

  • 簡単な計算を実行してみたところ

    簡単な計算を実行してみたところ

このようにREPLを使うとClojureの文法を手軽に確認することができます。

Hello, World!を実行してみよう

それでは、プログラムファイルを作って、コマンドラインから実行してみましょう。ここでは「Hello, World!」と表示するプログラムを作ります。以下のプログラムを「hello.cljs」という名前で保存しましょう。

(println "Hello, World!")

そして、コマンドライン上で、以下のコマンドを実行すると、Clojureのプログラムを実行できます。

$ lumo hello.cljs

コマンドを実行すると「Hello, World!」と表示されます。

  • コマンドラインからlumoコマンドでプログラムを実行したところ

    コマンドラインからlumoコマンドでプログラムを実行したところ

ClojureScriptでNode.jsの機能を利用しよう

なお、ClojureScriptでNode.jsの機能を使いたい場合、次のようにして利用できます。ここでは、Node.jsの標準モジュールのfsより、ファイル読み書きメソッドwriteFileSyncとreadFileSyncを利用してみます。

; Node.jsのfsモジュールにあるメソッドを得る --- (*1)
(require 
  '[fs :refer [
      writeFileSync
      readFileSync
  ]])

; ファイルの書き込み --- (*2)    
(writeFileSync "test.txt" "hello")

; ファイルの読み込んで画面出力 --- (*3)
(println (readFileSync "test.txt" "utf-8"))

上記のプログラムを「readwrite.cljs」という名前で保存します。コマンドラインで以下のコマンドを実行すると「test.txt」というファイルを作成し、その後「test.txt」の内容を読み込んで画面に「hello」と出力します。

lumo readwrite.cljs

プログラムの(*1)ではメソッドの利用を宣言し、(*2)ではファイル「test.txt」へ文字列「hello」を保存します。そして、(*3)ではファイルの内容を読み込んで画面に表示します。このように、手軽にJavaScriptの機能が利用できるので便利です。

FizzBuzz問題を解いてみよう

本連載では恒例となっていますが、FizzBuzz問題をClojureScriptで解いてみたいと思います。FizzBuzz問題とは次のような問題です。

1から100までの数を出力するプログラムを書いてください。ただし、3の倍数のときは数の代わりに「Fizz」と、5の倍数のときは「Buzz」と表示してください。3と5の倍数の時は「FizzBuzz」と表示してください。

以下のプログラムを「fizzbuzz.cljs」という名前で保存しましょう。

; FizzBuzzを定義 --- (*1)
(defn fizz? [n] (zero? (mod n 3)))
(defn buzz? [n] (zero? (mod n 5)))
(defn fizzbuzz? [n] (and (fizz? n) (buzz? n)))
(defn fizzbuzz-str
  [n]
  ; 順に条件を判定する --- (*2)
  (cond
    (fizzbuzz? n) "FizzBuzz"
    (fizz? n) "Fizz"
    (buzz? n) "Buzz"
    :else (str n)))

; 100回繰り返しFizzBuzzを実行 --- (*3)
(dotimes [i 100]
  (println (fizzbuzz-str (+ 1 i))))

そしてコマンドラインで「lumo fizzbuzz.cljs」を実行すると次のように1から100までのFizzBuzzの値を表示します。

  • FizzBuzzを実行したところ

    FizzBuzzを実行したところ

プログラムの(*1)ではFizzBuzzの各条件を関数として定義します。defnを使って関数を定義します。この例のようにClojureでは関数名に「?」も含めることができます。(*2)の部分ではcondを利用して順に条件を判定していきます。条件式に合致すれば条件の直後の値を返します。(*3)ではdotimesを利用して繰り返し100回fizzbuzz-strを実行します。

比較的冗長に書いたつもりでしたが、さすがClojureです。LISPのプログラムに見慣れていないと何をやっているのか分かりづらい点はあるかもしれませんが、とても処理がシンプルに記述できていることには同意できるのではないでしょうか。

ClosureScriptでサーバアプリを作ろう

そして、せっかくサーバーとクライアントの両方で動かせるClosureScriptですから、FizzBuzzのプログラムをサーバーアプリにしてみましょう。以下のプログラムを「server.cljs」という名前で保存しましょう。

; モジュールを取り込む --- (*1)
(def http (js/require "http"))
(use '[clojure.string :only (join)])

; FizzBuzzを判定する関数を定義 --- (*2)
(defn fizzbuzz-str [n]
  (cond
    (zero? (mod n 15)) "<b>FizzBuzz</b>"
    (zero? (mod n 3))  "<b>Fizz</b>"
    (zero? (mod n 5))  "<b>Buzz</b>"
    :else              (str n)))

; サーバーにアクセスがあった時の処理 --- (*3)
(defn handler [req res]
  (do
    ; 出力するFizzBuzz文字列を作成 --- (*4)
    (def s (join "<br>"
      (map fizzbuzz-str (range 1 100))))
    ; クライアントに本文を送信 --- (*5)
    (.end res
      (str "<html><body>"
        "<style>b{color:red}</style>"
        "<h1>FizzBuzz</h1>" s "</body></html>"))
    (println s)
  ))

; サーバーを起動 --- (*6)
(def server (.createServer http handler))
(.listen server 8000)

そしてコマンドライン上で『lumo server.cljs』を実行してみましょう。そしてWebブラウザ上で実行結果を確認してみましょう。Webブラウザで『localhost:8000』にアクセスします。するとWebブラウザにFizzBuzzの結果が出力されます。

  • FizzBuzzサーバーを作ってみたところ

    FizzBuzzサーバーを作ってみたところ

これは、Webサーバーを起動し、アクセスがあったらFizzBuzzのHTMLを出力するというプログラムになっています。詳しく確認してみましょう。(*1)ではhttpモジュールの取り込みを記述します。(*2)ではFizzBuzzの条件に応じてHTMLの値を返すようにします。先ほどのFizzBuzzのプログラムでは分かりやすく別途FizzBuzzの条件を関数にしましたが、ここでは直接条件を記述しました。

(*3)ではWebサーバーに対するアクセスがあったときの処理を記述します。(*4)ではFizzBuzzの結果を出力するHTMLを生成して、(*5)ではHTMLヘッダと共にクライアントへ文字列を送信します。そして、(*6)ではWebサーバーをポート8000で起動します。

まとめ

以上、今回はClojureについて紹介しました。実用的で堅実なLISP系のプログラミング言語Clojureは、JVMだけでなく.NetやJavaScriptで使えます。同じ言語でサーバーもクライアントもバッチ処理も全部書けるのもメリットです。LISPの良さを実用的なプログラムに活かせます。あまりLISPを書いたことはないという人でも、気分転換に挑戦してみると楽しいことでしょう。

自由型プログラマー。くじらはんどにて、プログラミングの楽しさを伝える活動をしている。代表作に、日本語プログラミング言語「なでしこ」 、テキスト音楽「サクラ」など。2001年オンラインソフト大賞入賞、2004年度未踏ユース スーパークリエータ認定、2010年 OSS貢献者章受賞。技術書も多く執筆している。直近では、「シゴトがはかどる Python自動処理の教科書(マイナビ出版)」「すぐに使える!業務で実践できる! PythonによるAI・機械学習・深層学習アプリのつくり方 TensorFlow2対応(ソシム)」「マンガでざっくり学ぶPython(マイナビ出版)」など。