具有語法規則的衛生和引用透明的巨集

LISP 和 Scheme 相比其他主流程式語言的最大優勢是它們的巨集系統。與 C 前處理器和其他巨集語言不同,Scheme 巨集將解析後的程式碼作為輸入,並將擴充套件程式碼作為輸出返回。這是 Scheme 的程式碼是資料短語的應用之一,它使語言如此強大。

Scheme 中的巨集是用 define-syntax 建立的,define-syntax 可以通過多種方式定義巨集。最簡單的方法是使用 syntax-rules,它使用模式匹配將輸入程式碼轉換為輸出程式碼。

此示例建立一個簡單的 for item in listfor list as item 語法,用於迴圈列表中的元素:

(define-syntax for
  (syntax-rules (in as) ; 'in' and 'as' keywords must match in the pattern
    ; When the 'for' macro is called, try matching this pattern
    ((for element in list
          body ...) ; Match one or more body expressions
     ; Transform the input code
     (for-each (lambda (element)
                 body ...)
               list))
    ; Try matching another pattern if the first fails
    ((for list as element
          body ...)
     ; Use the existing macro for the transform
     (for element in list
          body ...))))

然後可以按如下方式使用這兩個巨集,從而提供更強制的風格:

(let ((names '(Alice Bob Eve)))
  (for name in names
    (display "Hello ")
    (display name)
    (newline))
  (for names as name
    (display "name: ")
    (display name)
    (newline)))

執行程式碼將提供預期的輸出:

Hello Alice
Hello Bob
Hello Eve
name: Alice
name: Bob
name: Eve

需要注意的最常見錯誤是沒有將正確的值傳遞給巨集,這通常會導致無效的錯誤訊息應用於擴充套件形式而不是巨集呼叫。

上面的 for 語法定義不檢查它們是否傳遞了識別符號和列表,因此傳遞任何其他型別將導致指向 for-each 呼叫而不是 for 呼叫的錯誤。除錯這會破壞巨集的目的,因此由使用者將檢查放在那裡並報告使用錯誤,然後可以在編譯時捕獲。