こまぶろ

技術のこととか仕事のこととか。

関数型プログラミング未経験者がREPLでClojureに触ってみた

下記の記事を読んでいたら、Clojureというプログラミング言語の名前がちょこちょこと登場しているのが目に入り、少し調べてみることにした。

techracho.bpsinc.jp

Clojureとは

Clojureとはどんな言語なのだろうか。Wikipediaをみてみる。

Clojure (発音は/'klouʒər/[2], クロージャー)はプログラミング言語であり、LISP系の言語の方言の一つである。関数型プログラミングのプログラミングスタイルでのインタラクティブな開発を支援し、マルチスレッドプログラムの開発を容易化する汎用言語である。

LISP系の言語の方言」とある。LISPというのは名前だけ聞いたことがある程度だ。プログラミング言語で「方言」というと、SQL方言が真っ先に思い浮かぶが、LISPというのも標準語と複数の方言が存在する言語なのだろうか、とLISPについて調べ始める。なるほどLISPというのは歴史のある言語で、計算機プログラムの構造と解釈』(SICPという有名な教科書でも「Scheme」というLISP方言の言語が使われているようだ。

SICPは、プログラマ、というか計算機科学に関わるならば読んでおきたい書籍という扱いがされているようだ。日本語版のPDFでも700ページを超える重厚なもので、読み切るのは大変だろうが、とても勉強になりそうというのもまた確か。感想記事にはたくさんのはてブが付いている。いつか読んでみたい。

kinokoru.jp

と、やっているとキリがないので、本題に戻って、Clojureを少し触ってみることに。しっかり環境構築をしようと思うと最初で挫折するので、手軽に試してみようと考え、公式ドキュメントで紹介されているREPLによる入門をやってみることにした。

Clojureの対話型実行環境「REPL」

様々な言語にREPLが存在するが、ClojureにもREPLがある。これは、ClojureのCLIツールに同梱されているので、簡単に使い始めることができる。Mac環境では、ClojureをHomebrewでインストールすれば使えるようになる。

$ brew install clojure
$ clj
Clojure 1.10.0 
user=> 

これで、REPLが立ち上がり、Clojureのプログラミングがでできるようになっている。下記の公式ドキュメントにしたがって、いくつか操作を試してみる。

パッとみて、括弧が激しく使われるなぁ、と思っていたら、REPLでは)を入力したときに対応する(がハイライトされるようだ。これで少しは助かる。

f:id:ky_yk_d:20190126160330g:plain

Clojureでは+も関数

さて、手始めに足し算か、と思ったら、足し算からしてまず違う。演算子は前置記法で書く。

user=> (+ 2 3)
5

これは、関数と同じ書き方になるということでもある。というか、Clojureでは+は立派な関数らしい。Javaがメインのプログラマにとって、演算子は通常のメソッドとは違う特別な存在だが、このClojureでは演算子も関数として定義されている。C++では演算子オーバーロードできるし、プログラミング言語によって、同じような機能を持っているものの位置付けが違うのは面白いなと思う。

関数を定義するdefnは「マクロ」

次は関数の定義。ここでは階乗の関数を定義している。

user=> (defn factorial [n]
(if (= n 0)
  1
  (* n (factorial (dec n)))))
#'user/factorial
user=> (factorial 10)
3628800

defnというのは関数ではなく、「マクロ」というものらしい。C言語から入っているので、マクロというと、#defineなのだけど、LISPのマクロは一味違うらしい。なるほどわからん

ifは評価値を持つ「特殊形式」

また、面白かったものとして、ifがある。C言語Javaで制御構造の一つをなしているifが「文」を構成する(評価された値を持たない)のに対し、Clojureifは関数のように評価された値を持つ「式」ということになる*1Clojureifは厳密には関数ではなく、「特殊形式」というものに当たるらしい。

Clojureifは、(if test then else)という書き方をして、testの評価値がnilfalseでない場合はthenを評価し、nilfalseの場合はelseを評価するというものになっている。関数的な言い方をすると、引数を3つとるわけだが、これはC言語Javaの条件演算子Hoge ? Fuga : Piyoに似ている。

CとJavaを学んだときは、「なんだよこれわかりにくいな、なんでこんなんあるんだ」と感じ、最近は、「これ使うと行数少なく書ける」くらいに感じていた条件演算子だったが、関数型の世界でのifがこのような形になるのはなるほど自然で、にわかに条件演算子が由緒正しいものに思えてきた。

APIドキュメントを表示するマクロdoc

また、REPLには、docという便利な機能がある。(doc name)とREPLで入力すると、nameで指定したもののAPIドキュメントが表示される。いくつか例を示す。

f:id:ky_yk_d:20190126225447p:plain
「+」の説明も存在する

f:id:ky_yk_d:20190126225520p:plain
「doc」自体はマクロ

この機能があれば、REPLを使っていて関数などの使い方がわからなくなったときも、わざわざブラウザでドキュメントを見る必要がない。とても便利な機能だと感じた。

Clojureにおける「REPL駆動開発」

このdocをはじめとして、ClojureのREPLは単なる「入門用の遊び場」に止まらない機能を有している。そのためか、Clojureにおいては「REPL駆動開発」が書籍でも言及されているほどらしい。

感想

プログラミング言語は、C言語Java(Stream API不使用)と学び始めて、それからある程度書いたことがあるのもPythonRubyJavaScript(TypeScript)あたりで、関数型言語は全く経験がない。Scala去年の8月に入門的レベルをほんの少しだけ触ってそのままになっていて、本格的に関数型らしいところまでたどり着かなかったので、今回は初っ端から関数型らしさを感じられて面白かった。

現在、約1年振りにJavaを仕事で書くことになり、前回は使わなかったStream APIも使い始めている。先日のJJUGのイベントで、GoFデザインパターンの「振る舞いに関するパターン」をラムダ式で置き換えるというライブコーディングを見たこともあり、関数型プログラミングに対する関心が高まっている。オブジェクト指向もよくわかっていないのだけれど、せっかくStream APIを使えるのだから、関数型の考え方も取り入れながらプログラムが書けたらいいなと考えている。

計算機プログラムの構造と解釈 第2版

計算機プログラムの構造と解釈 第2版

プログラミングClojure 第2版

プログラミングClojure 第2版

*1:触ったことがある言語では、RubyScalaのifも「式」だった。