syntax-property 與 local-expand 的實際運用
syntax-property
可以用來把資訊存入 syntax 中,這可以用在各種有趣的
macro 擴展系統之中。一個我比較熟悉的案例是 type-system,比如我們可以寫下
(define-syntax-parser Nat [_ (syntax-property #''Nat 'type #'Type)]) (define-syntax-parser zero [_ (syntax-property #''zero 'type #'Nat)]) (define-syntax-parser succ [(_ n) (check-type #'n #'Nat) (syntax-property #'`(succ ,n) 'type #'Nat)])
來表示
data Nat = zero succ Nat
其中 check-type
可以用
(define (typeof stx) (syntax-property (local-expand stx 'expression '()) 'type))
來取得型別。 這是因為有一個常見的問題:marco
是由外往內處理的,然而我們的程式是要模擬由內往外的語意。這時候我們就會不知道
#'n
到底是什麼玩意兒,這裡用 local-expand
先展開之後,才會知道它原始的 syntax 是什麼。所以如果我們直接寫:
(define-syntax-parser succ [(_ n) (displayln (syntax-property #'n 'type)) (syntax-property #``(succ ,#,(local-expand n 'expression '())) 'type #'Nat)])
我們會得到 #f
而非 #'Nat=。而 =#'Nat
之巧妙就在於這時候如果我們寫:
(syntax-property (local-expand (syntax-property (local-expand #'n 'expression '()) 'type) 'expression '()) 'type)
會得到 =#'Type=。