2008年07月28日 10:00 [Edit]

LLからCL? - 書評 - 実践Common Lisp

共訳者の佐野様より献本御礼。

なんでこういう Common Lisp 本がなかったかという、さまざまな意味で21世紀的な Lisp本。LL、特に「P言語」の読者は必読。CLに走るせよ、LLに留まるにせよ。


本書「実践Common Lisp」は、英文学科(と便宜的に訳すが、ずばりEnglish)を卒業してジャーナリストとなった後、Perlプログラマー、Javaプログラマーを経てCommon Lispプログラマーとなった異色の「第二世代 Common Lisper」である著者が、「P言語」(perl/python/ruby)およびJavaプログラマーに向けて書いたCommon Lisp入門の邦訳。原文はWeb公開もされている。

目次 - Ohmshaより
第1章 序論:なぜLispなのか?
第2章 お気の済むまで:REPLツアー
第3章 実践:簡単なデータベース
第4章 シンタックスとセマンティクス
第5章 関数
第6章 変数
第7章 マクロ:標準的な制御構文の構築
第8章 マクロ:自分で定義しよう
第9章 実践:ユニットテストフレームワーク
第10章 数字、文字、そして文字列
第11章 コレクション
第12章 リスト処理:やつらがLISPと呼ぶ理由
第13章 リストを越えて:コンスセルの別用途
第14章 ファイルとファイルI/O
第15章 実践:パスネーム可搬ライブラリ
第16章 オブジェクト指向再入門:総称関数
第17章 オブジェクト指向再入門:クラス
第18章 FORMATの手習い
第19章 例外処理を越えて:コンディションと再起動
第20章 特殊なオペレータ
第21章 大域的プログラミング:パッケージとシンボル
第22章 黒帯のためのLOOP
第23章 実践:スパムフィルタ
第24章 実践:バイナリファイルのパース
第25章 実践:ID3パーサ
第26章 実践:AllegroServeでWebプログラミング
第27章 実践:MP3データベース
第28章 実践:Shoutcastサーバ
第29章 実践:MP3ブラウザ
第30章 実践:HTML生成ライブラリ ― インタプリタ版
第31章 実践:HTML生成ライブラリ ― コンパイラ版
第32章 結論:さて次は?
付録A 日本語処理
付録B 訳語一覧

本書を読んで最も驚くのは、むしろ「P言語」やJavaの使い手ではなく、schemerではなかろうか。その違いは、目次を見ただけで伺い知ることが出来る。せっかくなので「プログラミングGauce」のそれと比べて見よう。

目次 - 404 Blog Not Found:(use gauche); 書評 - プログラミングGaucheより
はじめに
第1部 予備知識
1章 LispとScheme
2章 Gaucheの特徴
3章 Gaucheの設計思想や誕生の背景
第2部 Gaucheの基礎練習
4章 Gaucheの開発スタイル
5章 プログラムの書式
6章 リスト
7章 手続き
8章 真偽値と条件判断
9章 状態の管理
10章 テストと例外処理
11章 評価モデル
第3部 実用的なプログラミング
12章 数値
13章 文字と文字列の処理
14章 入出力
15章 テキストの構築
16章 さまざまなデータ構造
17章 総称関数とオブジェクト
18章 構文の拡張
19章 継続
20章 モジュールシステム
21章 デバッグ
22章 データベース
23章 日付と時間
第4部 Webアプリケーションへの応用
24章 GaucheでCGIスクリプトを書いてみる
25章 スケジュール管理アプリケーション
26章 継続渡しによるWebアプリケーション
27章 Kahuaアプリケーションを書こう
付録A Gaucheインストール
付録B Emacsの設定
付録C Kahuaインストール
付録D GaucheとR6RS
索引

一目見てわかるのは、「プログラミングGauche」では第三部になってやっと登場するマクロが、いきなりのっけから登場することである。その代わり、Lispのイロハのイであるはずのリストに関する言及は、第12章にやっと登場するのだ。

コードもまた違う。Schemeを特徴づける再帰は、本書ではほとんど見られない。その代わり本書でびしばし登場するのが、マクロである。実際に定義もするのだが、それよりも多様な標準装備のマクロの多用が目立つ。その中でも際立って目立つのがloopマクロの多用だ。おそらく本書に登場する述語(predicate)の中で最も多いのではないだろうか。

Schemerなら、のんびりと

(define (from-to from to)
  (if (> from to) '() (cons from (from-to (+ from 1) to))))
(from-to 1 10)

とするか、あるいは華麗に

(use srfi-1) ; gaucheなら
(iota 10 1)

とするかという感じになるかと思うのだが、本書では

(loop for i from 1 to 10 collecting i)

である。lispというよりはPythonやHaskellの内包表記だ。もし本書がオライリーから上梓されていたら、表紙は同じ魚でもフムフムヌクヌクアプアアどころかバラキューダだったのではないか。

本書で最も評価したのは、著者がLL言語に対して「アウェイ戦」を挑んでいること。本書には Common Lisp コードだけではなく、要所要所に Perl や Python や Java のコードが登場する。Lisperというと仙人という印象がどうもあるが、本書には肉感がある。Matz的に言えば「血が滴るような」あの感じである。

こういうガチな一冊が、欲しかった。ガチなプログラマーであれば、必ず手応えを得ることができるだろう。

で、ここからは私見。

それでもなお、いやだからこそ、Lispが実践の場で多数を占めることはないだろう。私はかつてそれはLispersの選民思想によるものだと考えていたが、本書を読了して、また「フムフムヌクヌクアプアア本」をはじめ他の本も「つられ読み」して、どうもそうではない、少なくともそれだけではないと実感した。それを実感したのは、本書でCLOSのおさらいをしていたときだ。

Lispは、その構造上、オブジェクト指向は極められても、オブジェクト思考には向かないのだ。なぜか。述語が必ずリストの頭となるからだ。

Lisp と LL の関係について - 黎明日記
もしそれを望むならば、こんなふうにも書ける :
(define-class <cat> (<class>) ())
(define-class <dog> (<class>) ())

(define-class <x> () () :metaclass <cat>)
(define-class <y> () () :metaclass <dog>)

(define-method speak ((_ <cat>)) (print "meow!"))
(define-method speak ((_ <dog>)) (print "woof!"))

(for-each speak (list <x> <y>))
これが出来る言語はかなり限られているだろう。

ところがPerl 5にもわけなくできる。

Run via CodePad
#!/usr/local/bin/perl                                                           
use strict;
use warnings;
{
    package Animal;
    sub new { bless {}, shift }
    package Cat;
    use base 'Animal'; # inherits new()                                         
    sub speak { print "meow!\n" }
    package Dog;
    use base 'Animal'; # inherits new()                                         
    sub speak { print "woof!\n" }
}

$_->speak for map { $_->new } qw/Cat Dog/;
speak $_  for map { new $_ } qw/Cat Dog/;

そしてこの点が重要なのだが、Perl 5ではmethod $objectとCLOSのようにも書けるし、$object->methodとその他大勢の、OOP言語のほとんどが採用している、目的語=objectを書いてその後ろに述語=methodを書くというやり方の双方が使える。

で、どちらが推奨かといえば、双方の書き方が出来るPerlでもやはり後者の方なのだ。

オブジェクト指向を採用する理由は多々あるが、「まず対象を決めて、それから何をするかを決める」というオブジェクト思考をそのまま実践するという理由は、心理的には最大の要因だと思われる。「まず対象を決める」、これがあるからこそさほどの手だれでなくともメソッドチェインを書けるのだ。

本書で実は一番残念だったのは、著者が明らかにRubyを知らないこと。「P言語」の一つとして言及はされているのだが、言及のみで対比コードは登場しない。Rubyを知っていたら、マクロ相当の機能の欠如はまさに格好の売り文句となっていたのに。これはPythonも同様なのだが、Pythonに関しては"There is only one way to do it"ということが売り文句になっており、本書もそのことをきちんと指摘している。ちなみにPerl 5はuseがあるので、Lispのマクロ相当のことは相当できるし実際に行われている。Perlのオブジェクトシステムそのものが実は総称関数をベースとしたものだし、MOPもきちんと存在する。Mooseはその格好の事例である。

本書を読んで、Perl 5 がその実装においていかに Common Lisp に近いかを改めて実感した。dynamic scope と lexical scope の使い分け。総称関数によるOOPの実装。そしてマクロ実装の装置としてのuse....Perl 5こそ、幻のM式の実装例であり、そして Perl 6 がそれを完成させる....とは言い過ぎだろうか。

S式の最大の弱点は、括弧ではない。述語の位置が常に頭にあることなのだ。しかし同時にこれはS式の最大の利点でもあるのだ。構文木の直接表記は、強力かつ簡潔なマクロを可能とした。しかし1 + 1と書きたいのが、そしてobj.methodと書きたいのが自然人でもあるのだ。

しかし、Lispには出家を誘うだけの魅力があり、そしてCommon Lispには出家しても飢え死にしないだけのものがあるというのは、本書からびしばし伝わってくる。Lispは肉体言語でもあったのか....

`(dan the ,@language programmer)

追記:なぜforthはもっと普及しなかったのだろう。これまた述語の位置が変えられなかったからだろう....


この記事へのトラックバックURL

この記事へのトラックバック
書評リンク - 実践Common Lisp
実践Common Lisp【書評リンク】at 2008年08月01日 22:22
S式は言ってみれば言語の構文木そのものです。普通の言語では、処理系のフロントエンドにある構文解析器が、「人間に優しい」文法を「機械が理解しやすい」構文木に変換します。ということは、Lispは機械にやらせれば良い構文解析をわざわざ人間がやっているってことになりま...
Lisperの思考形態は違うのかもしれない【猫とプログラミング】at 2008年07月31日 11:13
この記事へのコメント
補足
http://www.lisp.org/HyperSpec/Body/glo_o.html#operator
http://www.lisp.org/HyperSpec/Body/glo_p.html#predicate
Posted by at 2008年07月29日 02:38
s|CLに走るせよ|CLに走るにせよ|
Posted by typo at 2008年07月29日 02:33
s/述語/オペレータ/
Posted by at 2008年07月29日 00:29