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

相关文章
相关标签/搜索