Gauche の extended-pair を使ってみよう

Gauche には extended-pair というものがあります。普段使っている分には気づきませんが実は裏で extended-pair はひっそりと活躍をしています。

早速触ってみましょう。以下のようなコードを書いて exp.scm と名付けました。

(define-macro (import-only module . syms)
  `(begin
     ,@(map (lambda (sym) `(define ,sym (with-module ,module ,sym))) syms)))

(import-only gauche.internal extended-pair? extended-cons pair-attribute-get pair-attribute-set! pair-attributes)

(let1 p '(1 2 3 4)
  (write p)
  (print (debug-source-info p))
  (when (extended-pair? p)
    (print "extended-pair")
    (print (pair-attribute-get p 'source-info))
    (pair-attribute-set! p 'source-info 'hoge))
    (print (pair-attribute-get p 'source-info))
    (pair-attribute-set! p 'oreore "秘密")
    (print (pair-attributes p)))

let1 より上は extended-pair を操作できるようにするためのおまじないです。


さて p は何の変哲もないリストです。 write して外部表現を表示してもそのままですね。

(write p)
=> (1 2 3 4)


まずは debug-source-info という手続きでソースコード情報を取り出してみます。これはマニュアルにも載っていますし使ったことがある人もいるかもしれません。

(debug-source-info p)
=> ("./exp.scm" 8)

ソースファイル名と行番号が得られました。これは便利!。


このソースコード情報はどこに埋められているのでしょうか?。実はこのリストで使われている pair はただの pair ではなく extended-pair であるという部分に秘密があります。
extended-pair とは car/cdr の他にもう一つ情報を持つことの出来る pair です。ソースコード情報はこの extended-pair 内に保持されているのでした。
なので p にたいして extended-pair? 手続きを適用すると #t が返ります。


extended-pair は基本的に pair と全く同様の振る舞いをしますが専用の手続きを介して、そこから情報を取り出したり格納することができます。
情報の格納形式は a-list、つまり (key . value) のリストで保持されています。

pair-attribute-get で特定の key に対する value を取り出したり

(pair-attribute-get p 'source-info)
=> ("./exp.scm" 8)

pair-attributes ですべて取り出したり

((source-info . ("./exp.scm" 8)))

値を set したりできます。

(pair-attribute-set! p 'oreore "秘密")
(pair-attributes p)
=>
((oreore . "秘密")(source-info . ("./exp.scm" 8)))

新しく extended-pair を作ることもできます。

(extended-cons 'a 'b)
=> (a b)

まとめ

ソースコード情報を保持するのに使われている extended-pair という仕組みを紹介しました。
extended-pair は pair と同じように振る舞いますが、任意の情報を alist の形で保持すること出来る便利な仕組みです。