生まれて3日目のUn-Common Lisp

| 【7分で読めるよ!】 | コメント(0) | トラックバック(0)

 Un-Common Lispは僕が知る限りで最も若い言語だ。それはだいたい3日前に生まれた。僕が作ったんだ。

 ほとんどの言語は、そのくらいの時期にはおそらくとてもひどく見えるだろうが、みんなに早くUn-Common Lispを見せて意見を得たかった。

 Common Lispは非常に表現力の高い言語だ。しかし、それに対して不満も多くある。僕のように不平を言いながらも他の言語に満足できずにしぶしぶ使い続ける人もいるし、少し覗いただけで立ち去ってしまう人も多い。

 Lisperは言う。不満があるなら書き換えればいい。Lispはそのための言語だ。Lispに足りないと思った機能があれば、いつでもそれを自分の処理系に追加できる。

 けれどそれではもはや追いつかないほどに皆が足りないと思う機能が増えてきた。僕はCommon Lispで自分のアプリケーションを書くときに、いつもutil.lispというファイルを作って、自分の.sbclrcから必要そうな関数やマクロ群をコピーしていることに気づいた。これはCOMMON-LISPパッケージが十分なユーティリティセットになりきれていない証拠だ。

 もちろんこれだけならば、(defpackage :oreore-utils)でも解決できた問題だ。言語を再発明する必要はない。しかし、Common Lispは歴史的な背景から見ても、慎重に設計された言語とは言えない。仕様のいくつかは古臭く、ときに饒舌で、ときに意味不明で、プログラマの手を煩わせる。

 そろそろLispは進化していい頃だ。Lisp界隈では、仕様が何十年も変わらないことがステータスのように思っている人もいるようだが、もはやそれはあまり魅力的ではなくなっている。実際いくつかの新しいLisp方言はCommon Lispの悪いアイデアを改善し、ユーザを獲得している。

 もしCommon Lispが、今まで言語の外にあった多くのアイデアを言語の中に取り込むならば、今よりずっとよくなるだろう。そして、僕はそれ以上のことを望まないことにした。最近のLisp方言には、秀逸で魅力的だが、一部の人間には受け入れられないような実験的な機能が含まれている。しかし、その多くは諸刃の剣だ。僕はCommon Lispを牙をなくした虎にするつもりはない。Common Lispは今まで通り高速性と抽象性を合わせ持つ唯一の言語であり続けるだろう。僕がすべきことは、その虎に水かきをつけてやることだ。

シンタックス

 lambdaは関数型言語を象徴する単語としていまや定着した。しかし、それがAlonzo Churchが考案したラムダ計算から由来するということはもはや豆知識ほどの意味しかない。

 UnCLにはlambdaはない。無名関数を作るには代わりにfnを使う。

(fn (x) (* x 2))

 Lispで無名関数がコード中に散りばめるように使われることを考えるとlambdaは長すぎるシンボルだ。代わりのfnはArcやClojureでも使われており、それを残念がっている人は僕の周りにいない。

 さて、僕はUnCLに実験的な機能を入れないと前置きした。しかし、実はひとつだけ実験的な機能を含む判断をしてしまった。さきほどの無名関数の例は以下のように書くこともできる。

[* _ 2]

 このシンタックスはArcから流用した。この表記に不平を言いたい人もいるだろう。その批判を受けてもなお、このシンタックスには以下の利点がある。

  • とても短い
  • シンプル
  • 目立つ

 角括弧は多くの言語で(Clojureでも)配列用のシンタックスとして使われている。しかし、角ばったものはLispの中ではとても目立つ。たかがデータ構造を表す表記としては大げさすぎる。

 一方、無名関数は変数のスコープを変え、前後の処理とは必ずしも繋がらない処理を含む。Paul Grahamは上記のシンタックスについてあまり多く語っていないが、この貴重な文字を無名関数に用いる視点は素晴らしい。この秀逸さは#L(* !1 2)と比較しても一目瞭然だ。

 また、UnCLにはもう一つ重要なシンタックスが追加されている。1..10(1 2 3 4 5 6 7 8 9 10)に展開される。

1..10
=> (range 1 10)
=> (1 2 3 4 5 6 7 8 9 10)

 こちらは多くの人に受け入れてもらえると信じたい。

強化されたdefmacro

 Lispに関する著書は挑戦的なものが多いように認識している人が多いようだ。「Let Over Lambda」はその最たるものだ。内容も賛否がわかれるだろう。僕は賛成派であり、UnCLの機能の多くはLOLが参考にされている。

 LOLの中で僕がお気に入りのマクロにdefmacro!がある。自動gensymや引数の評価を一度に限定(once-only)させる機能のついたdefmacroだ。UnCLにも同じ機能だが少し違うdefmacro*というマクロが付属する。

(defmacro* 1+10x (n%)
  `(let ((,num# (* 10 ,n#)))
     (1+ ,num#)))

 末尾に#のついたシンボルはgensymに展開される。また、仮引数の末尾に%をつければ、その引数は一度しか評価されない。

 実は1日前のUnCLでは、defmacroこそがまさにこの機能を持っていた。僕にとってもはやdefmacro!は標準といってもいいものだったからだ。しかし、プログラマの中にはマクロがシンボルに意味付けすることを気持ち悪く思う人もいることを知った。それから程なく、僕はこのマクロをdefmacro*という名前に変更した。もしdefmacro*が気に入らなければ慣れ親しんだdefmacroが使える。UnCLには標準でwith-gensymsonce-onlyが付属するので再発明する必要はない。

強化されたlet

 letlambdaと同じくらい使用頻度が高い。UnCLのletには洒落た機能はないが、括弧は1セット少なくなった。

(let (x 1
      y 3)
  (+ x y))
=> 4

 これはArcやClojureと同じだ。さらに言えばClojureのletはさらに高機能でもある。それはletを使うことを戸惑わせるほどだ。しかし使っているうちに、letでの分配機能は非常に便利だと気づいた。Common Lispで同じことをやりたければletの中でさらにdestructuring-bindを使わなければならない。

 UnCLのlet*はまさにClojureのものと同じ効果を持つ。

(let* (x 1
       (y z) (list x 20))
  (+ x y z))
=> (+ 1 1 20)
=> 22

アナフォリック

 アナフォリックマクロはマクロの正統な使い方から少しはみ出したものだ。その罪の意識があるからか、なかなか言語仕様の中に入れてもらうことができないでいるが、その効果が非常に高いことも認められつつあり、実際に多くのCLプログラムで利用されている。

 UnCLにはいくつかのアナフォリックマクロを取り込んだ。代表的なものはaifacondだ。ただし、名前はより一般的なものに変更した。

(if (find-user 10)
  (get-name it)
  'not-found)

(cond
  (zero? x) 1
  (> x 0)   it
  (< x 0)   (error "negative"))

 アナフォリックマクロで標準のマクロを上書きすることに葛藤がなかったわけではない。defmacro*のようにif*cond*にするという選択も考えた。しかし、プログラムを書いていて、最初はifで書いていたものを、あとでaifに書き直すといった修正をよくすることに気づいた。プログラマがifaifか判断するほどこの2つに重要な差はない。

セットアップ

 UnCLはSBCLで開発されているため、たぶんSBCLでしか動かない。

(require 'asdf-install)
(asdf-install:install "http://github.com/fukamachi/uncl/tarball/master")

使い方

 UnCLはCommon Lisp上に作られた言語だ。将来的に限界があれば土台からつくり直すかもしれないが、当面はCommon Lispプログラムとしてつくり続ける。そのためUnCLはCommon Lispプログラムに埋め込むように使うことができるし、既存のCLライブラリを使うこともできる。

 UnCLはCOMMON-LISPパッケージの代替物として提供したいと考えている。新しいアプリケーションのパッケージでUnCLを使いたいときは以下のように記述すればいい。

(defpackage your-package
  (:use :uncl))
(enable-uncl-syntax)

 既に見てきたように、UnCLはいくつかの組み込みのフォームを上書きしているため、同時に(:use :cl)するとコンフリクトしてしまう。ただ安心してほしい。UnCLではCOMMON-LISPにあった関数と同じものか、その代替物が提供されているため困ることはないだろう。

 もし自分のSBCLのトップレベルでUnCLを動かしたい場合は以下の3行を.sbclrcに追記すればいい。

(require 'uncl)
(in-package :uncl-user)
(enable-uncl-syntax)

これから

 まだUnCLはこれから多くの修正がされるだろう。比較関数(eq, eql, equal, equalpなど)はその違いが難しく初心者を惑わせるもとだ。また、ループ構文はこれから増えたり減ったりするだろう。正規表現にはより短く使いやすいシンタックスが必要だ。これらの変更は随時コミットしていこうと思う。

参考

トラックバック(0)

トラックバックURL: http://e-arrows.sakura.ne.jp/mt/mt-tb.cgi/162

コメントする

このブログ記事について このアーカイブについて

このページは、深町英太郎が2010年9月 9日 16:35に書いたブログ記事です。

ひとつ前のブログ記事は「Common LispでClojure風の無名関数を使う」です。

次のブログ記事は「一方、光プログラマーはCommon Lispを使った」です。

Ariel Labs
Name:深町英太郎
Age:23歳
Living:京都府
Company:はてな
Hatena Id:id:nitro_idiot
Facebook:eitarow.fukamachi
mixi:ID:6756132
Twitter:nitro_idiot
GitHub:fukamachi
LinkedIn:eitarowfukamachi

Techonrati

Technorati search

» リンクしているブログ

Powered by Movable Type 4.23-ja