拥有宏是否真的需要同质性?

时间:2012-02-04 20:43:05

标签: macros clojure lisp

2012-02-04由“homoiconicity”http://en.wikipedia.org/wiki/Homoiconicity赞助。

背景:我即将选择购买哪本关于Clojure的书 - "Clojure in Action"或(4月底传入)“Clojure Programming”(您可以通过{{3}阅读) ,一半页面可见)。令我印象深刻的是,在这两本书中,这种属性 - 同质性 - 得到了如此重视。

由于Clojure的根源是基于Lisp的,所以我提到O'Reilly Rough Cuts这真的很重要。好吧,我可以看到详细解释的宏,但我没有听到提到的重点。现在比较一下(引自“行动中的Clojure”)

  

这种同质性也是Clojure宏观系统成为可能的原因。

几乎看起来没有它,宏是不可能的。甚至维基百科声明(上面的链接)也更加平衡,但这些来源(*)都没有计算人为因素而支持英语语法。

如果我没有弄错的话,可以在C#(例如)中使用宏语法(类似于Lisp),只需要C#团队付出更多努力。但这是设计师团队的成本,而不是用户(?)。第二 - habbits很重要。如果在现实生活中你认为“a + b”,并且在计算机世界中你经常把它翻译成“+ ab”,那么生产力就会受到影响(当我从C ++仿函数转到C#lambdas时,我可以看到这一点。)

这个赞美,Clojure程序员几乎直接将程序编写为AST,让我感到害怕,因为阅读“由于直接用十六进制代码编写代码,不仅你学习十六进制系统,而且你更接近机器”。

总结一下 - 我喜欢元编程,我喜欢类似Lisp的宏(虽然我不是Lisper),但我在这里看到两件事 - 宏和同质性。第一个毫无疑问是伟大的,第二个 - 不是那么多(就我的理解而言),因为它使人类适合计算机需求,而且它应该是另一种方式。

问题

同性恋真的对人类(语言的最终用户)有益吗?或者它实际上几乎只对语言设计者有益?非常欢迎例子!

或者以防我改写 - 假设给定的语言有Lisp-macros,“增加”同质性会提高最终用户的生产力吗? Expresiveness?还是相反?

(*)我不能百分百肯定,因为我只看到一小部分Clojure书籍,我不是在阅读它们,只是评估它们的购买。

更新

谢谢大家的答案,可惜我只选择一个作为解决方案:-),这并不意味着我更少重视别人,这对我来说是最完整的。

5 个答案:

答案 0 :(得分:10)

不,你不一定需要同质性来创建一个强大的,类似Lisp的宏系统。同质性只是使这样一个系统更容易使用;没有它,你需要另一种语法来表达宏在评估时产生的AST。在同性语言中,宏观系统感觉很自然,因为程序和元程序看起来是一样的。

答案 1 :(得分:9)

宏并不严格要求同源性(例如:C / C ++预处理器实现了编译时宏系统)。

然而,homoiconcity使宏 更有效且易于使用:

  • 您无需语法分析步骤:在Clojure等同源语言中,源代码可以直接用作编译器的抽象语法树。减少编译器的性能开销,减少用户的概念开销。
  • 代码生成更容易 - 您只需要组装正确的数据结构,而不是以可以成功编译的形式仔细生成文本源代码。通常,文本代码生成在非为其设计的语言中可能是一个非常棘手的问题(您需要考虑许多事情,如符号名称生成,库导入,引用规则,编译器是否需要写入源文件磁盘等。)
  • 您的宏语言与源语言相同。无需为宏学习单独的语言或构造集。
  • 恕我直言同性恋使宏更加简单,优雅和可读。在许多情况下,您的宏最终看起来就像正确代码的模板一样。

作为一个小例子,这里是一个宏,它为Clojure添加了一个Java / C#样式“for”循环。

(defmacro for-loop [[sym init check change :as params] & steps]
 `(loop [~sym ~init value# nil]
    (if ~check
      (let [new-value# (do ~@steps)]
        (recur ~change new-value#))
      value#)))

;; Usage:
(for-loop [i 0 (< i 10) (inc i)] 
  (println i))

就是这样 - 一个新的语言结构添加在六行代码中。你可以清楚地看到同音性 - 整个“循环”形式只是一个引用的Clojure数据结构。 init,check和change参数也是传递给宏的homoiconic clojure数据结构,表示循环控制表达式。没有必要进行任何解析来支持我的新语法。

我认为用任何非同性语言简洁或简单地做这件事是非常困难的。

答案 2 :(得分:5)

同质性是一个定义不明确的概念。

Lisp的不同之处在于它具有源代码的数据表示,并且求值程序将其作为输入。将源代码作为数据处理很容易,因为Lisp的所有常用功能都适用。

它提供的内容:

  • 阅读文本源并创建它的数据结构
  • 将来源作为数据处理
  • 将数据源打印为数据&#39;作为文本,包括漂亮的代码打印之类的东西

宏是转换源代码的函数。因此,您只需要对源代码进行一些表示,并在编译语言的执行/编译/解释中执行此转换步骤。

Lisp的工作方式使其变得方便,因为源代码是一种数据结构,可以通过多种内置函数和工具进行操作。请注意,更复杂的转换(需要理解Lisp语言的语法)在Lisp中也不容易做到 - 虽然已经实现了工具(例如所谓的代码行者)。 / p>

还有一些Lisps具有语法,而不是基于s表达式。一个例子是RLISP,计算机代数系统 REDUCE 的实现语言。 RLISP也支持宏。

可以基于字符串和字符串操作定义类似的方案。您还可以根据某种AST定义宏系统。

具有此类宏的语言示例:

  1. 迪伦。请参阅:http://opendylan.org/books/dpg/macros.html
  2. 朱莉娅。请参阅:https://en.wikipedia.org/wiki/Julia_(programming_language)
  3. Nemere。请参阅:https://github.com/rsdn/nemerle/wiki/Macros-tutorial

答案 3 :(得分:3)

想象一下,使用使用<% code %>在python中使用嵌入式java的古老而笨拙的Web模板语言方法。

然后宏语言将是java,目标语言将是python。调用宏意味着转出<% code %>模式。您甚至可以设计这样的系统来支持参数,字符串的eval等。这将是真正的丑陋(有点像旧的jsp或PHP代码)。

一个可怕的想法:一些jsp用户是否会发现这样的系统比lisp系统更舒服?如果宏/目标语言相同,这个怪物会改进吗?

当然可以做到。人们用C ++模板做了一些了不起的事情。 Jay Freeman用Ackermann编写了一个C ++程序编译时间和不变的运行时间来证明C ++模板可以做这样的事情(大概是因为Fibonacci的例子不会炸掉足够的编译器?)。

至于同性恋:它并不能解决所有问题。即使有它,也常常有一些尴尬:行情,准引号等。你需要一些在“这是宏代码”和“这是目标语言代码”之间切换的方法。这是语言设计者需要解决的固有问题。您可以考虑使用eval方法使用字符串的烦恼,而不是使用列表作为数据结构或程序代码的lispy语言。程序源有点像字符串。字符串是一个非常可怕的数据结构。字符串中的字符串甚至更糟。查找任何quine代码(最好是非lisp)。他们可以被认为是非常错误的同性恋。

答案 4 :(得分:2)

虽然您需要一种用于宏的语言,但它不需要与编写其余代码的语言相同。您只需要“iconicity”*,“homo”部分是可选的。

在Ocaml中,宏用另一种称为camlp4

的语言编写

看起来something like this

#load "pa_extend.cmo";

value gram = Grammar.gcreate (Plexer.gmake ());
value exp = Grammar.Entry.create gram "expression";

EXTEND
  exp:
  [ "SUM"
    [ x = exp; "+"; y = exp -> Op "+" x y
     | x = exp; "-"; y = exp -> Op "-" x y]
  | "PROD"
    [ x = exp; "*"; y = exp -> Op "*" x y
    | x = exp; "/"; y = exp -> Op "/" x y]
  | "ATOM"
    [ x = INT -> Int (int_of_string x)
    | x = LIDENT -> Var x
    | "("; x = exp; ")" -> x ] ]
  ;
END;

它正在转换的语言如下所示:

let rec search ?(x=0) ?(y=0) f accu = match x, y with
    9, y -> search ~x:0 ~y:(y+1) f accu (* Next row *)
  | 0, 9 -> f accu                      (* Found a solution *)
  | x, y ->
      if m.(y).[x] <> '0' then search ~x:(x+1) ~y f accu else
        fold (fun accu n ->
                let n = Char.chr (n + 48) in
                if invalid x y n then accu else
                  (m.(y).[x] <- n;
                   let accu = search ~x:(x+1) ~y f accu in
                   m.(y).[x] <- '0';
                   accu)) accu 1 10

正如您所看到的,Ocaml中没有同质性,并且它具有高度发展的宏(类似)系统。

  • 这不是一个真正的术语,我希望有人会微笑:)