世に関数型言語と名のつくプログラミング言語はたくさんあります。 Lisp, Scheme, FP, etc... unlambdaはそれらの言語の中でも、 オブジェクトが関数しか存在しないという徹底した「純粋」関数型言語です。
Unlambdaを特徴付けるもう一つのものが、その強烈な難読性です (Unlambdaでプログラムを書くのはそれほど難しくはありません……あくまで読むのと比較して、ですが)。 何しろ、関数や変数に名前をつけるという事すらできないのですから、 後から読み返してその内容を理解するのは、ほぼ「不可能」です。
実際に、その難読性を体感していただきます。 次のプログラムは、アスタリスク(*)を216個連続して出力する「だけ」のプログラムです。
```s``si`k.*`ki```s``s`k``s`ksk``sii``si`k``s``s`kski``s``s`ksk``s``s`kski
実感していただけたでしょうか。とても読めたものではありません。 書いた本人である私も、これを明日見せられて意味が判るかと聞かれれば、 間違いなくノーと答えるでしょう。
ことほどさように、保守性ゼロなこのUnlambdaですが、書いている分には頭の体操にもなってなかなか楽しいです。 この項では、Unlambdaのチュートリアルと題して、Unlambdaの基本的な使い方を解説します。
ある関数F
に、引数A
を与えて実行することを、
「引数A
で関数F
を呼び出す」と言います。
C言語なら、これはF(A)
と書き、Lispなら(F A)
と書きます。
Unlambdaにはオブジェクトが関数しか存在しない上、行える操作はこの関数呼び出しだけです。 これでプログラムが組めてしまうのだからすごいものです。
Unlambdaでは、関数の呼び出しは「`」(バッククォート)で行われます。 先ほどの例をUnlambdaで書くと、
`FA
となります。
もう少し、複雑な例を示しましょう。
引数A
で関数G
を呼び出した結果を引数として、
関数F
を呼び出す例は以下のようになります。
`F`GA
同様の操作をC言語風に書くとF(G(A))
、Lisp風に書くと(F (G A))
となります。
また、Unlambdaの特徴として、全ての関数は一つだけ引数をとり(この引数もまた関数です)、 値を一つだけ返します(この返り値もまた関数です)。 引数をとらなかったり、値を返さない関数というものは存在しないのです。
xのところには任意の1バイト文字が1つ入ります。 この関数は、任意の一文字を出力する関数です。 引数は何でも良くて、引数はそのまま返り値となります。 例を見てみましょう。
`.a.b
関数.a
を、引数.b
で呼び出しています。
このコードの出力は以下のようになります。
a
任意の引数.b
を、文字aを出力する関数.a
に渡して呼び出した結果、
文字aが出力されました。ここで、.b
はただの引数で、関数呼び出しは行われていないので、
文字bは出力されません。
また、改行を出力する関数として、r
が提供されています。
使い方は、.x
と全く同じです。
それでは、プログラミング入門のお約束、Hello, world!プログラムを書いてみましょう。
`r`````````````.H.e.l.l.o.,. .w.o.r.l.d.!.a
バッククォートの数がすごい事になってますね。先頭から読んでいきましょう。
まず、関数r
が呼び出されようとしています(まだ呼び出されない)。
その引数として、`r
以降の部分が評価されます。
すると、この引数部分は、
.H
に引数.e
を与えて呼び出す……
文字Hが出力され、.e
が返される.e
に引数.l
を与えて呼び出す……
文字eが出力され、.l
が返される.l
に引数.l
を与えて呼び出す……
文字lが出力され、.l
が返される.!
に引数.a
を与えて呼び出す……
文字!が出力され、.a
が返される
という流れを経て、
最終的にr
の引数部分は.a
に評価されます。
ここではじめて最初のr
が呼び出され、改行が出力されます。
ちなみにこの.a
は、関数として呼び出されていないのでaは出力されません。
関数i
は、与えられた引数をそのまま返すだけの関数です。
一見役に立たないように見えて、実はかなり使います。
ですがここでは、毒にも薬にもならない使用例を示します。
``i.ai
このプログラムは、単純に文字aを出力します。解説する必要も特にないでしょう。
関数k
は、二つの引数を受け取り、二つ目の引数を捨て、一つ目の引数をそのまま返します。
はて、さっき全ての関数は一つしか引数をとらないと書いたじゃないか?
と思った貴方、いい所突いてます。
この関数の実際の使用例は、次のようになります。
``kAB
上の式を実行すると、B
は捨てられて、A
が返されます。
よく見ると、k
に二つバッククォートが付いているのに気づくはずです。
この式は、実際には
`kA
が評価され、
別の関数kA
(名前は適当です)が返される`kAB
が評価され、
B
が捨てられてA
が返されるという流れになります。一引数関数の組み合わせで、擬似的に二引数関数を実現しているわけですね。
二番目の引数を捨てるといっても、評価しないわけではありません。次の例を見てみましょう。
``ki`.ai
この関数は、文字aを出力します。kの二番目の引数が評価される際に、
関数.a
が引数i
で呼び出されるので、
文字aが出力されるのです。
k
はよく、定数関数を作るのに使われます。
以下のようにして定数関数を作ります。
`kA
これで、どんな引数で呼び出されても必ずA
を返す関数が作れます。
関数s
は、三つの引数を受け取ります。
仮にX, Y, Z
の三つの引数をとったとすると、
引数Z
で関数X
を呼び出し 、
引数Z
で関数Y
を呼び出し、
そして後者の返り値を引数として前者の返り値の関数を呼び出します。
言葉では判りづらいので、実際に書いてみましょう。 次の二つの式は同義です。
```sXYZ ` `XZ `YZ
具体的な例でも見てみましょう。
```skir
は、評価されて次のような式になります。
``kr`ir
すると、関数k
に二つの引数r
と`ir
(=r
)が渡されて呼び出されます。
そして二番目の引数が捨てられて一番目の引数が返されるので、
この式の値はr
となります。
s
とk
は非常に強力な性能を秘めています。
実はこの二つの関数で、i
は記述可能なのです。
``skk
この式は、i
と完全に同義です。試しに、引数A
で呼び出してみましょう。
```skkA ` `kA `kA A
……とまぁ、見事にA
がそのまま返ってくるわけです。
焼け石に水のような気もしますが、Unlambdaのプログラム中にコメントを含めることができます。 Unlambdaのコメントは、#から行末までの範囲になります。
例えば、`FA
という記述があった場合
(F
もA
も、単一関数でなく
`ki
のような式かもしれません)、
まずF
が評価され、次にA
が評価され、
最後に`FA
が評価されます。
この順序は、一部の例外的な関数(d
)を除いて全ての関数に当てはまります。