OnLispに出てくるanaphoric ifの代わりになる一般的な技、ありませんか? non-hygienicなマクロが気持ち悪い(勝手に"it"が束縛されているのはよくな い)というのは分かるんですが、じゃあどうするかというと、aifを使うのに比 べて冗長な書き方をするしかないような気がします。
場合によっては、condの"=>"構文が、aifの代わりに使えます。典型的には、 連想リストに値があればその値を返し、なければ#fを返すというような場合。 ローカル変数を省くことができます。
(cond ((assoc "foo" alist) => cdr) (else #f))
しかし、condを使えばいつでも簡潔に書けるというわけではありません。上の 例では、cdrの代わりにそこに複雑な手続きを置こうとすると、lambdaを書か なければならず、束縛フォームが現れて、見た目に複雑になってしまいます。 うまくいけばcutが使えますが、一般にそうとは限りません。
たとえば、ハッシュテーブルに値があれば、その値とそれを大文字にした値の リストを返し、ハッシュテーブルに値がなければ'()を返す手続きは、次のよ うに書くことになるでしょう。
;;; 書き方(1) (cond ((hash-table-get ht "key" #f) => (lambda (val) (list val (string-upcase val)))) (else '())) ;;; 書き方(2) (let1 val (hash-table-get ht "key" #f) (if val (list val (string-upcase val)) '()))
上記の書き方に比べると、下記のanaphoric ifを使った書き方のほうが明解で 簡単です。
(aif (hash-table-get ht "key" #f) (list it (string-upcase it)) '())
aifと同じくらい簡潔に書けて、なおかつ暗黙のitを使わなくて良いような方 法、ありませんか?
(or (and-let* ((val (hash-table-get ht "key" #f))) (list val (string-upcase val))) '())条件不成立時に返す値が#fであればorが省けます。 また、このパターンが頻出するならばマクロにするでしょう:
(define-syntax if-let1 (syntax-rules () ((_ var test then else) (or (and-let* ((var test)) then) else)))) (if-let1 it (hash-table-get ht "key" #f) (list it (string-upcase it)) '())
(define-syntax anaphoric (syntax-rules () ((_ syn var expr body ...) (let1 var expr (syn var body ...))))) (display (anaphoric and it (memq 1 '(1 2 3)) it)) (newline) (display (anaphoric and it (memq #f '(1 2 3)) it)) (newline) (display (anaphoric if it (= 1 0) it "not equal")) (newline) (display (anaphoric if it (= 1 1) it "not equal")) (newline)
(define-macro anaphoric (lambda (syn expr . body) (let1 it (gensym) `(let1 ,it ,expr (,syn ,it ,@(let rec ((body body)) (cond ((eq? body expr) it) ((pair? body) (cons (rec (car body)) (rec (cdr body)))) (else body)))))))) (anaphoric if #0=(memq 1 '(1 2 3)) (display #0#) (display "no menber")) (newline) (anaphoric if #0=(memq 0 '(1 2 3)) (display #0#) (display "no menber")) (newline)まあ、戯言なんだけどね。
(aif expr (a-macro-somebody-wrote ... it ...))ここのitはa-macro-somebody-wroteの書き方によって意味が変わって しまうわけで、そういう危うさを排除したい、という頑固さがSchemeには 感じられますね。