日曜プログラミング

休日趣味でやってるプログラミング関連記事をダラダラと

Clojure にはなぜ5つもクラス定義する方法があるのか

Stackoverflow に面白いトピックだったのでちょっと適当翻訳してみた。

元トピック:Why does Clojure have 5 ways to define a class instead of just one? - Stack Overflow

以下、翻訳


質問

Clojure でクラスっぽいデータ型の定義の方法として gen-class, reify, proxy, deftype, defrecord があります。プログラミング言語として構文の簡素化や不要な複雑さを避けると言う意味では良いのかもしれませんが、クラス定義でここまであるのはちょっとやりすぎな気もします。なぜこうなってるのでしょうか。Common Lisp ライクな defclass で十分なのではないでしょうか。*1

回答1

この質問について以下の 3 つの側面から回答します。

  1. JVM 固有のデータ型
  2. データ定義する際の、異なるユースケースでこのような異なる関数を定義する必要性*2
  3. いくつかは開発初期に作られ、その後発展している

これらを踏まえた上で、deftype と genclass は AOT コンパイル時に名前付きクラスを生成すると言う意味では良く似ています。genclass がまず用意され、その後 1.2 で deftype が追加されました。deftype の方が推奨されており、またパフォーマンスに優れていますが、制約もあります。deftype はインターフェースクラスとなり、他のクラスからの継承ができません。

reify と proxy は実行時の匿名クラスを動的に生成するのに使われます。これもまたまず proxy が用意され、その後 1.2 で deftype と共に reify が追加されています。reify も proxy より推奨されていますが、deftype ほど制約はありません。

最後に deftype と defrecord の違いについてですが、これらは同時期に導入されており、役割もまた似ています。ほとんどの場合 Clojure を使う利点を知っているならば defrecord を使う方が良いです。deftype はもっと低レベルのデータ構造向けのものです。通常の Clojure とのインターフェースは持っていませんが、オプションとしてミュータブルなフィールドを持つ事が可能です(デフォルトはオフですが)。

回答2

簡単に答えるとそれぞれ使用すべき場面はあります。根本的な複雑さは JVM と効果的に相互運用す
る事に起因します。

Java との相互運用を考えなければ defrecord と Clojure の map との関係に悩まされる事は 99% ありません。

  • protocol を使う場合は defrecord を使う
  • でなければ Clojure の map の方が最もシンプルで理解しやすいです

また、どのデータ定義を使う必要があるか選択する時は以下のフローチャートが参考になります。
http://cemerick.com/2011/07/05/flowchart-for-choosing-the-right-clojure-type-definition-form/



翻訳以上。

回答 2 のフローチャートは自分も大いに参考になったんでこちらにも再掲。

*1:訳注: Common Lisp の defclass はよー知りません

*2:超意訳。