scheme 宏macro寫法

scheme 宏macro寫法

 

來源 https://www.cnblogs.com/GodWroteinlisp/archive/2012/03/27/2420426.htmlhtml

scheme目前其實有至少三套宏系統:linux

  1. 徹底hygiene的syntax-rules(define-syntax)
  2. 能夠hygiene也能夠dirty的syntax-case(define-syntax)
  3. 逐步被淘汰的lisp傳統宏系統define-macro(有些實現裏叫defmacro)
syntax-rules的能力是受限的,不能引入新的syntax-object,只能寫一些簡單的宏.可是用syntax-rules寫出來的宏確定比用syntax-case或define-macro寫的更優雅.

 syntax-case徹底不受限制,擴展能力與傳統lisp宏(defmacro)是同樣的,但因爲它自帶模式匹配功能,因此寫起來會更方便,至少quasiquote,unquote,unquote-splicing少了不少.程序員

由於syntax-case的強大能力,用它來實現define-syntax 也是很簡單滴:web


(define-syntax define-macro
  (lambda (x)
    (syntax-case x ()
      ((define-macro (name . params) body1 body2 ...)
       #'(define-macro name (lambda params body1 body2 ...)))
      ((define-macro name expander)
       #'(define-syntax name
           (lambda (x)
             (syntax-case x ()
               [(name . args) ;(_ . args) more hygiene!!
                (datum->syntax #'name (apply expander (syntax->datum #'args)))]))))))) 

以上代碼證實了scheme的清潔宏系統至少和傳統lisp宏系統同樣強大.express

那麼,能不能用非清潔宏系統來實現清潔宏呢?這篇文章有介紹:Hygiene for the Unhygienic編程

 

 

scheme裏的宏不一樣的實現有不一樣的寫法安全

來源 https://www.cnblogs.com/youxin/p/3427323.htmlapp

 

1.mzscheme的define-macro (mzscheme也就是pltschme,也就是drracket,沒有define-macro這個關鍵字)編輯器

語法:(define-macro   macro-nameide

          (lambda  macro-args)

            macro-body   ......)

例如:定義when

(define-macro   when

     (lambda    (test .   branch)

        `(if    ,test

         (begin    ,@brach))))

其中「·」重音號引入模版,逗號開始的符號爲參數,逗號和@開始的被當爲列表。

2.MIT的define-syntax和syntax-rules

語法:(define    macro-name

          (syntax-rules   ()

           ( (template)  operation)

            ......)  )

上面的when的定義:

(define-syntax     when

    (syntax-rules ()

       ((when  test   expr  ...)  (if   test     (begin  expr  ...)))))

when的定義很是簡潔,主要是這種語法的模版很是直觀,其中「...」就能夠表示有多個參數。

 

r5rs文檔裏面寫的:

Syntax definitions are valid only at the top level of a <program>. They have the following form:

(define-syntax <keyword> <transformer spec>)

<Keyword> is an identifier, and the <transformer spec> should be an instance of syntax-rules. The top-level syntactic environment is extended by binding the <keyword> to the specified transformer.

There is no define-syntax analogue of internal definitions.

Although macros may expand into definitions and syntax definitions in any context that permits them, it is an error for a definition or syntax definition to shadow a syntactic keyword whose meaning is needed to determine whether some form in the group of forms that contains the shadowing definition is in fact a definition, or, for internal definitions, is needed to determine the boundary between the group and the expressions that follow the group. For example, the following are errors:

(define define 3)

(begin (define begin list))

(let-syntax
  ((foo (syntax-rules ()
          ((foo (proc args ...) body ...)
           (define proc
             (lambda (args ...)
               body ...))))))
  (let ((x 3))
    (foo (plus x y) (+ x y))
    (define foo x)
    (plus foo x)))

 

Drracket宏:

http://docs.racket-lang.org/guide/macros.html

  1. 最簡單的建立一個宏的方式是使用 define-syntax-rule :
    (define-syntax-rule pattern template)
    
    ;; 一個具體的例子,交換兩個變量的值
    (define-syntax-rule (swap a b)
      (let ([tmp a])
        (set! a b)
        (set! b tmp)))

    pattern中的symbol成爲 pattern variable, 在template中全部的pattern variable會被具體的 實際調用時候的語法對象所替代。使用:

    (define x 1)
    (define y 2)
    (swap x y)
    x :2
    y:1

  2. The define-syntax-rule form binds a macro that matches a single pattern. The pattern must always start with an open parenthesis followed by an identifier, which is swap in this case. After the initial identifier, other identifiers are macro pattern variables that can match anything in a use of the macro. Thus, this macro matches the form (swap form1form2) for any form1 and form2.
  3. 在drracket下:
  4. #lang racket
     
     (define-syntax-rule (swap a b)
       (let ((tmp a))
         (set! a b)
         (set! b tmp)))
    (let ((x 1)(y 2))
      (swap x y) (list x y))

     

define-syntax 和 syntax-rules :

define-syntax 和 syntax-rules : define-syntax-rule 只能匹配一個pattern,可是使用define-syntax和syntax-rules,咱們 能夠在一個宏裏寫出多個pattern-template。

  1. 咱們 能夠在一個宏裏寫出多個pattern-template。
    (define-syntax id
     (syntax-rules (literal-id ...)
       [pattern template]
       ...))
    
     ;;一個具體的例子
     (define-syntax rotate
       (syntax-rules ()
         [(rotate a b) (swap a b)]
         [(rotate a b c) (begin
                           (swap a b)
                           (swap b c))]))
    

    pattern能夠支持sequence  , 用來表示一個或者多個syntax object.

    (define-syntax rotate
      (syntax-rules ()
        [(rotate a) (void)]
        [(rotate a b c ...) (begin
                              (swap a b)
                              (rotate b c ...))]))
    
  2. procedure macro: 使用syntax-rules咱們不能對pattern variable作更多判斷(譬如判斷macro的參數是否合法等),不能對 template作更多操做。
    (define-syntax swap
     (lambda (stx)
      (syntax-case stx ()
        [(swap x y)
         (if (and (identifier? #'x)
                  (identifier? #'y))
             #'(let ([tmp x])
                 (set! x y)
                 (set! y tmp))
             (raise-syntax-error #f
                                 "not an identifier"
                                 stx
                                 (if (identifier? #'x)
                                     #'y
                                     #'x)))])))
    

    這裏對swap參數作了檢查,若是這樣調用 (swap 10 b) 將會報錯,由於10不是一個identifier

  3. transformer: define-syntax 建立一個 transformer ,且綁定一個名字,這個綁定的名字能在編譯的時候 用來展開表達式(expand expression)。
    (define-syntax a
      (lambda (stx)
        #'(printf "zh\n")))
    
    (a)
    

    固然transformer通常使用syntax-rules定義,syntax-rules返回的是一個procedure:

    (syntax-rules () [(nothing) something])
    #<procedure>
    
    (syntax-case #'(+ 1 2) ()
      [(op n1 n2) #'(- n1 n2)])
    
    '(- 1 2)
    

3 Hygienic macros

Hygienic(安全)是對Scheme Macro系統描述用的最多的一個詞,通常來講,hygienic macros用來表示 表示宏的展開式裏引入的變量不會和宏所使用的環境中的變量名衝突。

一個比較準確的描述:

If a macro transformer inserts a binding for an identifier, the new binding will 
not capture other identifiers of the same name introduced elsewhere.

舉例來講:

(define-syntax-rule (swap a b)
  (let ([tmp a])
   (set! a b)
   (set! b tmp)))

(let ([tmp 100] [b 200])
  (swap tmp b)
  (printf "~a\n" (list tmp b)))

(swap tmp b) 展開後定義了一個局部變量 tmp ,他們會swap所使用的環境中的 tmp 不會有 任何關係,他們不會發生衝突。

另外一個經常使用來形容scheme macro特色的詞彙是 referential transparency ,若是一個宏展開式 中引用了一個free variable(非local variable), 那麼這個free variable將和宏定義的環境綁定, 而和宏具體使用環境無關。這有點像lexical scoping。

(define-syntax-rule (swap a b)
  (let ([tmp a])
   (set! a b)
   (set! b tmp)))

(let ([set! 100] [b 200])
  (swap set! b)
  (printf "~a\n" (list set! b)))

在swap的定義裏使用了let, set!這兩個free variable,他們綁定的是swap定義處的環境,爲global namespace。 在swap使用的環境中,能夠看到set!已經被定義成一個number,可是這不會對swap展開式中的set!有任何影響。

固然如今通常使用 hygienic macro 同時表示上面兩個scheme macro的特性。

4 Syntax Object:

macro transformer的輸入輸出爲syntax object。一個S-exp對應的syntax object包含了值:(quote S-exp), source-location,lexical-information(用來保證hygienic特性). source-location通常是parse 源代碼的時候 加入(看另外一篇文章racket reader). 建立一個syntax Object很簡單:

(syntax (+ 1 2))
#<syntax:1:0 (+ 1 2)>

5 C Macro

經過 #define 的形式定義的pre-processor,他是C語言的重要組成部分。C的宏能幫咱們作不少事情, 譬如定義常量,省些重複代碼,內聯代碼等。

6 Scheme Macro 與 C Macro的比較

C宏的優勢暫且不說,這裏只說下缺點。C的宏在某些狀況下,比較難以獲得安全,可靠的轉換後的代碼。

  1. C的宏容許咱們在宏的實現裏寫入任意字符串。
    #define foo "hello
    printf(foo world")
    

    這個宏連lexical tokens都不是完整的(此時一個完整的lexcial token爲"hello world"). 這對閱讀代碼,編輯器分析程序源碼都是很痛苦的事。咱們說這種宏:failed to respect the integrity of lexical tokens。

  2. C的宏能夠展開成任意詞法序列:
    #define mul(a, b)  a*b
    add(x-y, x+y)
    
    exand to:
    x-y*x+y
    

    正由於此,咱們在初次學習C的宏的時候,就會被告知,宏的實現體裏必定要把參數括起來!但即使如此, 我在實際工做中仍是出現了忘了括小括號,結果致使了錯誤。這種現象叫作:failed to respect the structure of expressions。

  3. 咱們在宏內使用的名字可能和宏使用的環境的名字衝突:
    #define swap(v, w) { int tmp = (v);\
         (v) = (w); (w) = tmp;}
    
    int tmp = 100;
    int atmp = 200;
    swap(tmp, atmp);
    

    因此,咱們在學習C宏的時候仍是被告知,宏內引入的名字(這裏譬如tmp)應該使用一個比較特殊的名字, 譬如_tmp.可是這隻能說是一個權宜之計,當swap這個宏在另外一個宏內使用,而令一個宏恰巧也使用了 _tmp這個變量,那麼仍是有可能形成錯誤。這種現象叫作:fail to respect the correlation between bindings and uses of names.

  4. 當宏的參數是一個expression的時候,可能有side effect
    #define discriminant(a,b,c)  ((b)*(b)-4*(a)*(c))
    
    discriminant(3, x--, 2)
    

    這種問題在C的宏系統裏沒法避免,只能靠程序員細心去避免。可是在scheme的宏裏,咱們能夠經過定義新的 變量的方式來避免這種問題。

7 總結:

Scheme Macro很是強大,安全,成熟,他也成爲不少其餘語言提供宏機制的參考之一。固然也有缺點,就目前個人認識,我認爲最爲困難的地方在於難以掌握,可是一旦掌握 了Scheme Macro背後的實現,不少難以理解的地方也就豁然開朗。以後我將在寫兩篇文章,一是深刻Scheme Macro,二是聊聊 Scheme Macro的實現。

 

轉自:http://blog.csdn.net/cnnzp/article/details/8307798

---------------------

一篇很是好的文章:

http://www.ibm.com/developerworks/cn/linux/l-metaprog2.html

 

用 Scheme 編寫 syntax-case 宏

syntax-case 宏並非 Scheme 的標準部分,可是它們是使用最普遍的宏類型,容許健康和非健康形式,與標準的 syntax-rules 宏密切相關。

syntax-case 宏採用清單 1 所示的形式:


清單 1. syntax-case 宏的通常形式

(define-syntax macro-name
   (lambda (x)
     (syntax-case x (other keywords go here if any)
       (
         ;;First Pattern
         (macro-name macro-arg1 macro-arg2)
         ;;Expansion of macro (one or multiple forms)
         ;;(syntax is a reserved word)
         (syntax (expansion of macro goes here))
       )
       (
         ;;Second Pattern -- a 1-argument version
         (macro-name macro-arg1)
         ;;Expansion of macro
         (syntax (expansion of macro goes here))
       )
 )))

 

這種形式將 macro-name 定義爲用於進行轉換的關鍵字。用 lambda 定義的函數由宏轉換器用來將表達式轉換爲展開形式。

syntax-case 以表達式 x 做爲第一個參數。第二個參數是關鍵字列表,這些關鍵字在語法模式中採用字面意義。模式中使用的其餘標識符用做模板變量。而後,syntax-case 接受一系列模式/轉換器組合。它依次經過每一個組合進行處理,嘗試將輸入形式與模式進行匹配,若是匹配的話,它就產生相關聯的展開形式。

咱們來看一個簡單的示例。假設咱們想編寫一個比 Scheme 提供的版本更詳細的 if 語句版本。還假設咱們想尋找兩個變量中比較大的一個並返回它。代碼以下:

(if (> a b) a b)

對於非 Scheme 程序員,沒有明顯的文字能夠指出哪一個是 「then」 分支,哪一個是 「else」 分支。爲了幫助他們理解代碼,能夠建立定製的 if 語句,添加 「then」 和 「else」 關鍵字。代碼以下:

(my-if (> a b) then a else b)

清單 2 演示執行此操做的宏:


清單 2. 定義擴展的 if 語句的宏

;;define my-if as a macro
(define-syntax my-if
  (lambda (x)
    ;;establish that "then" and "else" are keywords
    (syntax-case x (then else)
      (
        ;;pattern to match
        (my-if condition then yes-result else no-result)
        ;;transformer
        (syntax (if condition yes-result no-result))
       )
)))

 

在這個宏執行時,它按照如下形式將 my-if 表達式與模板進行匹配(換句話說,將宏調用與宏定義模式進行匹配):

(my-if  (> a b)  then     a      else    b)
   |       |      |       |       |      |
   |       |      |       |       |      |
   v       v      v       v       v      v
(my-if condition then yes-result else no-result)

 

所以,在轉換表達式中,任何出現 condition 的地方就替換爲 (> a b)(> a b) 是不是一個列表並不重要。它是包含列表中的一個元素,因此它在模式中做爲一個單元。產生的 syntax 表達式只是將每一個部分從新安排成一個新的表達式。

這種轉換髮生在執行以前,這個時期稱爲宏展開時期(macro-expansion time)。在許多基於編譯器的 Scheme 實現中,宏展開在編譯時發生。這意味着宏只在程序開始時或編譯時執行一次,之後沒必要再次執行。所以,咱們的 my-if 語句沒有運行時開銷 —— 它在運行時轉換爲一個簡單的 if

在下一個示例中,咱們要執行 swap! 宏。這個簡單的宏要交換兩個標識符的值。清單 3 給出了使用這個宏的示例。


清單 3. 使用 swap! 宏交換標識符的值

(define a 1)
(define b 2)
(swap! a b)
(display "a is now ")(display a)(newline)
(display "b is now ")(display b)(newline)

 

這個簡單的宏(清單 4)經過引入一個新的臨時變量來實現交換:


清單 4. 定義 swap! 宏

;;Define a new macro
(define-syntax swap!
  (lambda (x)
    ;;we don't have any keywords this time
      (syntax-case x ()
        (
          (swap! a b)
          (syntax
            (let ((c a))
              (set! a b)
              (set! b c)))
        )
)))

 

這個宏引入了一個名爲 c 的新變量。可是,若是要交換的參數之一正好也名爲 c,那麼會怎麼樣?

syntax-case 解決這個問題的辦法是在宏展開時將 c 替換爲一個唯一的未使用的變量名。所以,語法轉換器會本身負責這個問題。

注意,syntax-case 沒有替換 let。這是由於 let 是一個全局定義的標識符。

用不衝突的名稱替換引入的變量名,這種方法稱爲健康的(hygiene);產生的宏稱爲健康的宏(hygienic macros)。健康的宏能夠安全地在任何地方使用,沒必要擔憂與現有的變量名衝突。對於許多元編程任務,這個特性使宏更可預測並容易使用。

更多看原文。

更多:http://www.shido.info/lisp/scheme_syntax_e.html

 

============= End

相關文章
相關標籤/搜索