橋本 Hashimoto   Baku

橋本 Hashimoto   Baku

Jun 26, 2024: S式が好き (メモ)

このページは個人的なメモ書きです。何かあればご連絡ください。

Glispの文法をS式にこだわらずM式、あるいはC言語ライクにしても良かったのかもしれない。add[2; mul[3; 4]]、あるいはadd(2, mul(3, 4))のように。だけど、僕がS式で好きなのは、関数と引数との構文上の区別の薄さだ。

例えば、add(2, 5) という式を見た時に直感的に思い浮かべる構文木は

flowchart TB
add((add)) --> 2
add --> 5

※ 実際の内部表現は、 FunctionAppliation( Symbol(add), NumericLiteral(2), NumericLiteral(5)) あたりだろうけど。

だが、S式の(+ 2 5)の方はというと、関数も引数も、関数適用式 () の下に等しく並べられているイメージだ。括弧に括られたリストのうち、単に一番最初に来る要素が関数で、残りが引数として解釈される。

flowchart TB
App((@)) ==> +
App --> 2
App --> 5

※ 面白いことに、伝統的なS式のメモリ表現は (+ . (2 . 3)) のような連結リストになっているという点で、関数と引数とがC言語のAST以上に非対称だったりもする。

このような構文とすることで、引数に式をネストするのと同じくらい自然に、関数に式をネストしようと思える。

((if (== sign "+") + -) 2 5)
((=> x (+ x 1)) 2)

C言語ライクのような「関数名を括弧に前置する」構文だとこうはいかない。関数を返す式や無名関数を括弧で括ってあげたりと、ひと工夫必要になる。

;(sign === '+' ? add : sub)(2, 5)
;(x => x + 1)(2)
(add if sign == '+' else sub)(2, 5)
(lambda x: x + 1)(2)

S式の「関数と引数を並置する」構文は、関数適用するもの・されるもの隔たりを少しだけ埋めてくれる。そして引数と同様に、関数もまたプログラム上のネスト可能な式の一つに過ぎないということを、構文論的に僕らに思い出させてくれる。