如何使语言谐音


16

根据本文,以下Lisp代码行将“ Hello world”打印到标准输出。

(format t "hello, world")

Lisp是一种谐音语言,可以通过以下方式将代码视为数据:

现在假设我们编写了以下宏:

(defmacro backwards (expr) (reverse expr))

向后是宏的名称,该宏采用一个表达式(表示为列表)并将其反转。再次使用宏,这是“您好,世界”:

(backwards ("hello, world" t format))

当Lisp编译器看到该行代码时,它将查看列表(backwards)中的第一个原子,并注意到它为宏命名。它将未评估的列表传递("hello, world" t format)给宏,该宏将列表重新排列为(format t "hello, world")。结果列表将替换宏表达式,并且将在运行时对其进行评估。Lisp环境将看到它的第一个原子(format)是一个函数,并对其进行评估,并将其余的参数传递给它。

在Lisp中,完成此任务很容易(如果我错了,请纠正我),因为代码是作为列表实现的(s-expressions?)。

现在看看这个OCaml(不是谐音语言)片段:

let print () =
    let message = "Hello world" in
    print_endline message
;;

想象一下,您想向OCaml添加谐音,与Lisp相比,它使用的语法要复杂得多。你会怎么做?语言是否必须具有特别简单的语法才能实现谐音?

编辑:从该主题中,我找到了另一种实现同音的方法,该方法不同于Lisp的方法:用io语言实现的方法。它可能会部分回答这个问题。

在这里,让我们从一个简单的块开始:

Io> plus := block(a, b, a + b)
==> method(a, b, 
        a + b
    )
Io> plus call(2, 3)
==> 5

好的,所以该块起作用了。加号块将两个数字相加。

现在让我们对这个小家伙做一些自省。

Io> plus argumentNames
==> list("a", "b")
Io> plus code
==> block(a, b, a +(b))
Io> plus message name
==> a
Io> plus message next
==> +(b)
Io> plus message next name
==> +

热圣冷模具。您不仅可以获取块参数的名称。而且,您不仅可以获得字符串的完整源代码字符串。您可以潜入代码并遍历其中的消息。最令人惊奇的是:它非常简单自然。忠于艾欧的追求。Ruby的镜子看不到任何东西。

但是,哇,嘿,现在,不要碰那个拨盘。

Io> plus message next setName("-")
==> -(b)
Io> plus
==> method(a, b, 
        a - b
    )
Io> plus call(2, 3)
==> -1

1
您可能想看看Scala
Bergi 2016年

1
@Bergi Scala使用了一种新的宏方法:scala.meta
马丁·伯格

我一直都对同调性高估。在任何功能强大的语言中,您始终可以定义一个树结构,以反映该语言本身的结构,并且可以编写实用程序功能以根据需要与源语言(和/或编译形式)进行转换。是的,这是稍微在的Lisp更容易,但考虑到(一)绝大多数编程工作应该不会被元编程和(b)LISP牺牲了语言的清晰度做到这一点,我不认为代价是值得的。
Periata Breatta

@PeriataBreatta是的,但是MP的主要优点是MP可以实现抽象而不会增加运行时间。因此,MP解决了抽象和性能之间的矛盾,尽管以增加语言复杂性为代价。这值得么?我要说的是,所有主要的PL都有MP扩展名这一事实表明,许多正在工作的程序员发现MP提供的折衷方案很有用。
马丁·伯杰

Answers:


10

您可以将任何语言设为谐音。本质上,您是通过“镜像”语言来完成此操作的(对于任何语言构造函数,您都可以将该构造函数的对应表示形式添加为数据,请考虑使用AST)。您还需要添加一些其他操作,例如引用和取消引用。差不多。

Lisp早就因为它的语法简单而闻名,但是塔哈(W. Taha)的MetaML语言家族表明,对任何一种语言都可以做到。

建模齐次生成元编程概述了整个过程。这里是对相同材料的更轻量的介绍。


1
如果我错了纠正我。“镜像”与问题的第二部分有关(同语言中的同音性),对吗?
欣喜

@Ignus我不确定我是否完全理解您的评论。谐音的目的是使代码可以作为数据处理。这意味着任何形式的代码都必须具有表示形式的数据。有几种方法可以做到这一点(例如,ASTs准引用,使用类型(通过轻量级模块化暂存方法来区分代码和数据)),但是所有方法都需要以某种形式对语言语法进行加倍/镜像。
马丁·伯格

我认为@Ignus将从MetaOCaml中受益吗?那么,“同质”是否意味着被引用?我认为像MetaML和MetaOCaml这样的多阶段语言会更进一步吗?
史蒂文·肖

1
@StevenShaw MetaOCaml非常有趣,尤其是Oleg的新BER MetaOCaml。但是,它在某种程度上受到限制,因为它仅执行运行时元编程,并且仅通过准引号表示代码,而准引号的表达不如AST那样。
马丁·伯格

7

Ocaml编译器是用Ocaml本身编写的,因此,肯定有一种在Ocaml中操纵Ocaml AST的方法。

可能有人想象向ocaml_syntax语言添加一个内置类型,并拥有一个defmacro内置函数,该函数接受类型的输入,例如

f : ocaml_syntax -> ocaml_syntax

现在是什么类型defmacro?好吧,这实际上取决于输入,因为即使f是身份函数,结果代码的类型也取决于所传递的语法。

Lisp不会出现此问题,因为该语言是动态类型化的,并且在编译时无需将任何类型归因于宏本身。一种解决方案是

defmacro : (ocaml_syntax -> ocaml_syntax) -> 'a

这将允许在任何上下文中使用该宏。但这当然是不安全的,它将允许使用a bool代替a string,从而在运行时使程序崩溃。

静态类型语言中唯一有原则的解决方案是具有依赖类型,其中的结果类型defmacro将依赖于输入。不过,此时的情况变得相当复杂,我首先要向您介绍David Raymond Christiansen 的精彩论文

总而言之:具有复杂语法不是问题,因为有很多方法可以在语言内部表示语法,并且可能使用元编程(如quote操作)将“简单”语法嵌入到内部ocaml_syntax

问题在于使它具有良好的类型,特别是具有不允许类型错误的运行时宏机制。

当然可以使用Ocaml之类的语言为宏提供编译时机制,例如,参见MetaOcaml

也可能有用:Jane Street在Ocaml中进行元编程


2
MetaOCaml具有运行时元编程,而不是编译时元编程。此外,MetaOCaml的键入系统没有依赖类型。(还发现MetaOCaml是类型错误的!)模板Haskell有一个有趣的中间方法:每个阶段都是类型安全的,但是进入新阶段时,我们必须重新进行类型检查。根据我的经验,这在实践中确实非常有效,并且在最后(运行时)阶段,您不会失去类型安全的好处。
Martin Berger 2016年

@cody可以通过扩展点在OCaml中进行元编程,对吗?
2016年

@Ignus恐怕我对扩展点了解不多,尽管我确实在指向简街博客的链接中引用了它。
科迪

1
我的C编译器是用C写的,但并不意味着你可以操纵用C ...的AST
BlueRaja -丹尼Pflughoeft

2
@immibis:显然,但是如果那是他的意思,那么该陈述既是虚无,
也不

1

例如,考虑F#(基于OCaml)。F#并非完全谐音,但在某些情况下支持将函数代码作为AST获得。

在F#中,您print将被表示为Expr打印为:

Let (message, Value ("Hello world"), Call (None, print_endline, [message]))

为了更好地突出显示结构,这是创建相同方法的另一种方法Expr

let messageVar = Var("message", typeof<string>)
let expr = Expr.Let(messageVar,
                    Expr.Value("Hello world"),
                    Expr.Call(print_endline_method, [Expr.Var(messageVar)]))

我不明白。您是说F#让您“构建”表达式的AST,然后执行它?如果是这样,使用该eval(<string>)功能的语言有什么区别?(根据许多资源,具有评估功能不同于具有同音性 -这是您说F#不完全同音的原因吗?)
包括

@Ignus您可以自己构建AST,也可以让编译器完成。同声性“允许将语言中的所有代码作为数据进行访问和转换”。在F#中,您可以将某些代码作为数据来访问。(例如,您需要标记print[<ReflectedDefinition>]属性。)
svick
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.