Scheme の手続きを動的に #2

今日はふたつ賢くなった。

ひとつはScheme の #() と '#() が違うものだということを前回のエントリのコメントで教えていただいた。

いくつかの処理系で #() を評価させて試してみたところ、以下のようになった。

Ypsilon 0.9.6-update3 (R6RS)

> #()

error: invalid expression
  >  #()

expanding:
  >  (begin #())
  ..."/dev/stdin"

Gauche 0.9.3.3 (R5RS)

gosh> #()
#()

Mosh 0.2.7 (R6RS)

mosh> #()

Unhandled exception:

 Condition components:
 1. &message       message: "invalid expression"
 2. &syntax        form: #()
                   subform: #f

Racket 5.2.1 (R5RS)

Welcome to Racket v5.2.1.
> #()
'#()

Guile 1.8.7 (R5RS)

guile> #()
#()


R5RS の処理系は #() を受け付け、R6RS の処理系はきっぱりと拒む、という結果になった(偶然でしょうけど)。

プログラミング Gauche にも、16.1.1 に以下のように記述されていた。

ベクタリテラルを記述するときはクオートしてください。(クオートされていないベクタリテラルの振舞いは Scheme の規格では定義されていません)

プログラミングGauche

プログラミングGauche

R5RS にも以下のように書かれている。

リスト定数と同じく、ベクタ定数もクオートしなければならない


もうひとつは、クオートされているベクタ(やリスト)から取り出した値は、まだ評価されていないシンボルであるということ。

(symbol? (vector-ref '#(+ - * /) 2))

上記式は、今回試した 5 つの処理系ですべて #t になった。

そして当たり前だけど、手続きではなかった。

> (procedure? (vector-ref '#(+ - * /) 2))
#f

シンボルを手続きにするには評価すればよいので、以下のように行う。

gosh> ((eval (vector-ref '#(+) 0) (scheme-report-environment 5)) 1 2)
3

最初のやりたいことは以下のように書ける。

(define op '#(+ - * /))
(define calc
  (lambda (kind lhs rhs)
    ((eval (vector-ref op kind) (scheme-report-environment 5)) lhs rhs)))
(calc 0 1 2)

ただ、とても回りくどいので、この場合敢えてベクタ定数を使う必要がないということだと理解した。