破壊!

プログラミング言語 Scheme は多くの LISP 系言語がそうであるようにリスト操作を多用する。 SRFI-1 では便利なリスト操作手続きが定義されていているのだが、 RnRS の考え方とは異なる部分があり、そこで初心者がつまづくことがあるようだ。 以下のような事例で (C B A) が出力されることを期待してしまうといったようなことだ。

(import (scheme base)
        (scheme write)
        (srfi 1))

(define x (list 'A 'B 'C))
(reverse! x)
(write x)

実際には SRFI-1 によればこのコードの出力結果は規定されない。 参照実装通りの実装であれば (A) になるだろう。

Scheme の一般的な習慣としては名前の末尾にエクスクラメーションマークが付いている構文や手続きは破壊的な操作を行うのだが、 SRFI-1 においては破壊という言葉を使わずに線形更新 (linear update) という言葉を定義して当て嵌めている。 これは入力されたオブジェクトを壊して再利用する可能性があることを示すもので、どのように再利用されるか、あるいは再利用されないのかは規定されていない。 reverse! の結果はあくまで返却値であり、入力したオブジェクトはもはや利用してはならないということを意味する。

R5RS や R7RS ではそれぞれ以下のような命名規約が示されている。

規約により、割り当て済みの場所 (3.4 節参照) に値を格納する手続き名は通常の場合“!”で 終了している。 こういった手続きを変異手続き (mutation procedure)と呼ぶ。規約上、変異手続きの返す値は未規定である。

! は,以前に割り当てられた場所の中へ値を格納する手続き (3.4 節参照) の名前の最後の文字である。このような手続きは変異手続き (mutation procedure) と呼ばれる。 変異手続きが返す値は未規定である。

これらの規約に従うなら、名前の末尾がエクスクラメーションマークであるような手続きは破壊的な操作を行い、返却値が規定されない以上はその破壊的な操作こそが期待する動作であることを意味する。 この意味で考えていると reverse! の挙動につまづいてしまう。

このように SRFI のいくつかは RnRS と、あるいは他の SRFI と一貫していないこともあるので注意が必要だ。

Document ID: 81b51a480de15de54c3ce139310dcca6