簡體   English   中英

如何在Racket中使用宏重命名過程?

[英]How do I rename a procedure with macros in Racket?

假設我要在編譯時替換所有出現的過程,例如,用😄所有出現的cons 我嘗試了兩個看似自然的選擇:

1。

(define-syntax 😄
  (syntax-rules ()
    [(_ x y) (cons x y)]))

如果我做類似(😄 2 3)事情,這是(😄 2 3) ,但是如果我不在應用程序位置,則不能使用😄如果這樣做我會得到'語法錯誤'的錯誤(map 😄 '(1 2) '(3 4))

然后我想只替換標識符本身:

2。

(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的示例,聽起來像是您所追求的。

http://www.scheme.com/tspl4/syntax.html#./syntax:s61

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM