LoginSignup
8

More than 5 years have passed since last update.

新しいS式を考えてみました

Last updated at Posted at 2016-05-10

プロローグ

Lisp系言語はカッコが多いです。LispのソースコードはLispのデータである(同図像性)ということを一目見て実感します。いやはや、うっとりしますね。ずっと眺めていたい。とはいえ、慣れていない人間がその意味を追うためにソースコードを見たり書いたりする際に、スッと入ってこないことがあることがあるのも事実。Clojureをたまに書いたりする自分ですら、エディタのおかげで内容を読み取るのに困ることはないものの、S式そのものに不満がないわけでもありません。

そんな時に下記の記事を見ました。古い記事ですが「やっぱりそうだよね」としきりに頷いてしまいました。
https://practical-scheme.net/wiliki/wiliki.cgi?Lisp%3AS式の理由

以下、よく話題になる部分も含め、気になる部分を挙げてみます。

関数適用されるリストとデータとしてのリストがほぼ同じ外見

; b,c,dには数値が入っている
(sum b c d) ;関数適用
'(b c d) ;quoteされたリスト
(list b c d) ;関数適用だけど結果的にはリスト

何を今更、と思われるでしょうが、quoteはあるものの、知らない人にはやっぱりわかりにくいのでは。C言語の流れを汲む文法を持つ言語などでは、関数はカッコの外に出ていて、引数だけがまとめられます。個人的には、意味が違うものは違う見かけになってほしい気もします。「同図像性を保つには、これこそが最善」という声も頭の中で響くのですが、もうちょっとなんとかならんのかい、とも感じる自分もどこかにいます。

その点、Clojureは偉い。

(list b c d) ;リスト
[b c d] ;ベクタ
{:e "f" :g "h"} ;マップ

カッコの種類をフル活用しているおかげで、めちゃ読みやすいです。関数適用が行われるのはリストの時のみですし、最初見た時は軽く感動しました。もうこれでええんや!俺Lispとか作らんでもええ!

……としばらくは思っていたのですが、やっぱり、関数と引数が同じリストの中にいることがどうしても気持ち悪い。何をやっても所詮S式なのですが、「構文」まではいかない、S式の他の見かけは、考えられないんだろうか。もちろん、マクロを書く時に困らないほどにはS式の力を保ったままで(この点Elixirのマクロは惜しいと勝手に考えています)。

まず、「リストの最初の引数は関数、残りは引数、評価すると値が返却される」という大前提と戦わないといけない。「リストは評価すると関数が動きだす」のがデフォルトとも言えます。このデフォルトを、Clojureのベクタのような「評価してもデフォルトでは最初の引数が関数とみなされない」側に倒してみるとどうでしょう。

[1 2 3] ;=> 評価しても[1 2 3]のまま

従いまして、ふつーのリストだと関数適用っぽいものも、前提が変わると結果が変わります。

[sum 1 2 3] ;=> sumが変数扱いになるけれど、sum関数が走るわけではない

話を簡単にするためにキーワードに。

[:sum 1 2 3] ;=> 評価しても[:sum 1 2 3]

さて、ここからです。このベクタを外から関数適用したいのです。つまりapplyしたい。
仮に書くと、こんな感じでしょうか。

[:apply' [:sum 1 2 3]]

まあ、でもこれだと、評価してもapplyは走りません。だって、そういう前提で今考えているので。評価しても、データ構造はそのままです。だから、何か「apply的なことをする」、特別なオペレータを導入する必要があります。

いろんな方法があると思うのですが、私の頭の中には、Javaのアノテーションに似たものが浮かんでいました。例えばこんな感じで。

@apply [:sum 1 2 3]

もはやClojureではなくなったので、この角カッコでできたFormを仮にシーケンスと呼びますが、このようなアノテーションをシーケンスに付与することによって、データ構造に関数を適用できるルールにしよう、というアイディアです。そしてこのアノテーションは、概念上、シーケンスに属しています。言い換えると、必ず後続にシーケンスを必要とするということです。

つまり、「フォーム=リスト」と見做すのではなく、「シーケンスとアノテーション」という二つの基礎概念から成り立っていると見做すわけです。そして、applyアノテーションがシーケンスに付与されている時にのみ、関数適用が行われるルールにします。

関数と引数を分ける

ここからは、構文変換を行うことで構文らしきものを作っていきます。きっとリードマクロを使うことでしょう。(上記アノテーションもリードマクロで作れますけど、概念上の話ということで)

sum(1, 2, 3);

みたいにしたいので、迷いましたがドット(.)を構文に採用することにしました。

.sum[1 2 3] ;これをマクロ展開すると
@apply [:sum 1 2 3] ;こうなるイメージ 評価すると6になる

C言語っぽい見かけにしたいので詰めて書いてますがトークン的には

. sum [1 2 3] ;スペースを入れた

と分解されます。

(sum 1 2 (sum 4 5 6) (- 8 5)) ;他のlispだとこう
.sum[1 2 .sum[4 5 6] `[8 - 5]] ;新しい構文だとこう

これだけだと上の方がわかりやすいやん、みたいに感じてしまうかもしれません(実際私もそう感じました)が、コード量が増えてきて、いろんな構文を導入し始めると、だんだんその考えも変わってきました。なによりlispに馴染みのない人は下の方が意味を取りやすいはず。

密かにバッククオートとか出てきてますが、これで中値記法ができるつもり。

次回予告

自己満足全開、拙い垂れ流しの文章となりましたが、発展させて色々と考えました。力尽きたので続きはまたの機会に。S式への不満その2は「末尾の閉じカッコ弾幕」です。例は我らがバイブルOn Lispより。

(defun revc (x k)
  (if (null x)
      (funcall k nil)
      (revc (cdr x)
            #'(lambda (w)
                (funcall k (append w (list (car x)))))))) ;この末尾の閉じカッコ弾幕

Lisperはカッコを気にするな、インデントで読むんだ、エディタに任せておけばいいんだ、と言います。事実、私もそうやっております。それでも、この拭い切れないもやもや。カッコの数を、ちょうどよい塩梅で減らすにはどうすればいいんでしょう??

というテーマでいずれ書こうと思います。お付き合いいただきありがとうございました。ほなまた。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8