Scheme の begin と継ぎ合わせ

プログラミング言語 Scheme の構文である begin は直列化のための機能としてよく理解されている。 要するに複数の式を順番に評価させる (そして最後の式の評価結果を全体の評価結果として返す) ための構文である。 Common Lispprogn に相当するものと説明されることもある。

だが、 begin は状況によって別の解釈をされることがある。 R5RS の「5.1.プログラム」の項目で定義されているのだが、トップレベルに現れる begin フォームに限っては、その内側に書かれている式、定義、構文定義と等価であるとされている。 つまり、 (begin a b c) と書かれていたら、それはトップレベルに a b c と書かれたのと同じになるということだ。

具体例で表してみよう。

(begin
  (define (plus-one x)
    (+ x 1))

  (define (plus-two x)
    (+ x 2)))

(write (+ (plus-one 1) (plus-two 1)))

このようにトップレベルの begin の内側に書かれたものは以下と同等に解釈される。

(define (plus-one x)
  (+ x 1))

(define (plus-two x)
  (+ x 2))

(write (+ (plus-one 1) (plus-two 1)))

要するにトップレベルで begin に囲まれている個所は囲まれていないのと同じであるということだ。 この操作を begin の外側に「継ぎ合わせ (splicing)」られるという。 このことを知らないと begin の内側での定義は一見してローカルな定義に見えてしまいがちなので注意が必要である。

囲んでも囲まなくても同じならわざわざ begin で囲む意味などないだろうと思うかもしれないが、これは主にマクロへの配慮だと考えられる。 マクロはひとつのフォームを別のひとつのフォームに変換するものだが、複数のフォームへ変換したい場合もあるので見掛け上のひとつのフォームにするために begin が必要なのだ。

ちなみに、 R6RS や R7RS ではこの継ぎ合わせはトップレベルだけでなく <body> 部で機能するように拡大されているのと、意味の異なる二種類の構文であることがより明確に書かれている。 また、 R7RS には define-library の補助構文としての begin もある (つまりは三種類の begin がある)。 それらは同じ begin という名前でありながらも現れる場所によって違う解釈されることには充分に注意が必要である。

Document ID: 0a54a00152f1c3e16c15ea8e11b1ea7b