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には 感じられますね。