[英]How do I rename a procedure with macros in Racket?
假設我要在編譯時替換所有出現的過程,例如,用😄
所有出現的cons
。 我嘗試了兩個看似自然的選擇:
(define-syntax 😄
(syntax-rules ()
[(_ x y) (cons x y)]))
如果我做類似(😄 2 3)
事情,這是(😄 2 3)
,但是如果我不在應用程序位置,則不能使用😄
如果這樣做我會得到'語法錯誤'的錯誤(map 😄 '(1 2) '(3 4))
。
然后我想只替換標識符本身:
(define-syntax 😄
(λ (_) #'cons))
現在😄
本身給了我#<procedure:cons>
並且(map 😄 '(1 2) '(3 4))
給出了正確的答案,但是對於(😄 2 3)
,語法轉換器使用所有參數並替換了使用cons
表示整個表達式,這不是我想要的。
我如何實現自己想要的? 有某種變壓器可以做到這一點嗎?
更新:輸入最后一句話時,我調用了“橡皮鴨效果”,並找到了我想要的make-rename-transformer
。 但是,文檔說“可以手動編寫這樣的轉換器”,而且我的2次嘗試似乎都失敗了。 您如何手動制作這樣的變壓器? (忽略文檔中make-syntax-transformer
要點)
另外,我知道我可以只使用(define 😄 cons)
但這在運行時是一個額外的函數調用。
做到這一點的絕對最簡單(也許是最好)的方法是使用rename-in
以不同的名稱導入標識符:
(require (rename-in racket/base [cons 😄]))
這也是最直接的方法,因為它根本不會創建語法轉換器,而是會創建一個在所有方面都與cons
相同的新綁定,包括free-identifier=?
。 從編譯器的角度來看,這使cons
和😄
難以區分。
但是,這有點神奇。 另一種方法是使用make-rename-transformer
手動創建一個重 make-rename-transformer
:
(define-syntax 😄 (make-rename-transformer #'cons))
如果您不能使用rename-in
,那么使用freename轉換器是第二件事,因為free-identifier=?
函數會特別識別重命名轉換器,因此(free-identifier=? #'cons #'😄)
仍為#t
。 這對於關心語法綁定的宏很有用,因為😄
將在所有相同的位置接受cons
。
盡管如此,這仍在使用某種原始類型。 如果確實願意,您如何自己實現make-rename-transformer
? 好吧,關鍵是宏應用程序可以在Racket中以兩種形式出現。 如果您具有宏foo
,則可以通過以下兩種方式之一使用它:
foo
(foo ...)
當宏用作裸標識符時,第一種情況是“ id宏”,第二種情況是更常見的情況。 但是,如果要編寫一個與表達式一樣工作的宏,則需要擔心這兩種情況。 您不能使用不允許id宏的syntax-rules
來做到這一點,但是可以使用syntax-id-rules
來做到這一點:
(define-syntax 😄
(syntax-id-rules ()
[(_ . args) (cons . args)]
[_ cons]))
這種模式將使😄
在兩種情況下😄
按預期擴展。 但是,與上述兩種方法不同,它將不與free-identifier=?
合作free-identifier=?
,因為從宏擴展器的角度來看, 😄
現在是綁定到普通宏的全新綁定。
從這個角度來看, make-rename-transformer
魔力? 並非如此,但是就free-identifier=?
,這是特殊的free-identifier=?
作為特殊情況處理。 如果願意,可以實現自己的make-rename-transformer
函數和自己的free-identifier=?
函數獲得類似的行為:
(begin-for-syntax
(struct my-rename-transformer (id-stx)
#:property prop:procedure
(λ (self stx)
(with-syntax ([id (my-rename-transformer-id-stx self)])
((set!-transformer-procedure
(syntax-id-rules ()
[(_ . args) (id . args)]
[_ id]))
stx))))
(define (my-rename-target id-stx)
(let ([val (syntax-local-value id-stx (λ () #f))])
(if (my-rename-transformer? val)
(my-rename-target (my-rename-transformer-id-stx val))
id-stx)))
(define (my-free-identifier=? id-a id-b)
(free-identifier=? (my-rename-target id-a)
(my-rename-target id-b))))
這將允許您執行以下操作:
(define-syntax 😄 (my-rename-transformer #'cons))
…並且(my-free-identifier=? #'😄 #'cons)
將為#t
。 但是,出於明顯的原因,不建議您實際執行此操作:不僅不必要,而且由於其他宏將不使用my-free-identifier=?
,它也不會真正起作用my-free-identifier=?
,只是普通的free-identifier=?
。 因此,強烈建議您改用make-rename-transformer
。
如果您要手動編寫這樣的宏,則需要使用make-set!-transformer
。
http://docs.racket-lang.org/reference/stxtrans.html?q=set-transformer#%28def。 %28%28quote。 〜23〜25kernel%29._make集%21-變壓器%29%29
請注意,您需要同時處理賦值(set! xe)
和引用x
而應用程序(x arg0 arg1 ...)
需要由syntax-case
表達式中的子句處理。
UPDATE
在Dybvig的《方案編程語言》一書中,他有一個宏的define-integrable
的示例,聽起來像是您所追求的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.