Dylan语言

Dylan是多范型的编程语言,包括了支持函数式面向对象编程(OOP),它是动态反射式的,却提供了设计用于支持生成高效机器代码的编程模型,包括了在动态和静态行为上的细粒度的控制。它是在1990年代早期由苹果公司领导的群组创造的。

Dylan
编程范型多范型: 函数式, 面向对象, 多分派
语言家族Lisp
实现者苹果公司, Harlequin英语Harlequin (software company), 卡内基·梅隆大学
发行时间1992年4月,​32年前​(1992-04
当前版本
  • 2022.1(2022年11月28日)[1]
编辑维基数据链接
类型系统强类型, 动态
系统平台IA-32, x86-64
操作系统跨平台
文件扩展名dylan
网站opendylan.org
主要实现产品
Open Dylan, Gwydion Dylan
派生副语言
中缀dylan, 前缀dylan
启发语言
CLOS, ALGOL, Scheme, EuLisp英语EuLisp
影响语言
Lasso英语Lasso (programming language), Python, Ruby, Julia[2]

概述

在Dylan参考手册中有简明而彻底的语言概述[3]。Dylan派生自SchemeCommon Lisp,并增加了派生自Common Lisp对象系统(CLOS)的集成的对象系统。在Dylan中,所有的值(包括数值、字符、函数和)都是头等对象。Dylan支持多重继承多态多分派关键字参数英语Named parameter、对象内省、基于模式语法扩展宏和很多其他高级特征。程序可以表达在动态性上的细粒度的控制,允许程序占据在动态和静态编程之间的连续区,并支持演进式开发(允许先快速原型随后增进精制和优化)。

Dylan的主要设计目标是成为适合开发商业软件的动态语言。Dylan尝试解决潜在的性能问题,通过向完全灵活性的Lisp系统介入“本性”限制,允许编译器清晰的理解可编译单元比如函数库。Dylan从Scheme和其他Lisp派生出了它的很多语义;某些Dylan实现最初建造在现存Lisp系统之内。但是Dylan有着类似ALGOL的语法而非类似Lisp的前缀语法。

历史

Dylan是在1990年代早期由苹果公司领导的一个群组创建的。在它开发的时候,它被意图用于Apple Newton电脑,但是Dylan实现那时还没有达到充分成熟,而Newton转而使用了C和Walter Smith开发的NewtonScript二者的混合。Apple在1995年终止了其Dylan开发努力,尽管他们制作了一个可获得的“技术发行”版本(Apple Dylan英语Apple Dylan TR1),并包括了一个高级集成开发环境(IDE)。

其他两个小组对语言设计和开发实现做出了贡献:Harlequin公司英语Harlequin (software company)发行了Microsoft Windows下的商业IDE,卡内基·梅隆大学发行了叫作Gwydion Dylan的Unix下的编译器。二者的实现分别于2004年和1998年开放了原始码。Harlequin实现当前叫作Open Dylan并由一组志愿者维护。

Dylan语言的代号是Ralph。James Joaquin选择名字Dylan表示“动态语言”(Dynamic language)。

语法

Dylan的多数语法特征来自它的Lisp传承。Dylan最初使用类似Lisp的前缀语法,它基于了S-表达式。到了语言设计完成的时候,语法被变更为类似ALGOL的语法,预期广泛的编程者受众会更加熟悉它。语法由Michael Kahl设计。它在Dylan参考手册中有详尽描述[3]

词法

Dylan不是大小写敏感的。Dylan的词法允许使用连字暨减号的命名约定,来连接多单词标识符的各部分(有时叫做“lisp-case”或“kebab case”)。这个约定在Lisp语言中是常见的,但不适用于将不是数值文字英语Literal (computer programming)一部分的连字号暨减号,当作一个单一词法记号处理的那些编程语言,即使在它没有包围着空白字符的时候。

除了字母数字字符和连字暨减号之外,Dylan允许特定非字母数字字符作为标识符的一部分。标识符不可以单独的由非字母数字字符或数字字符组成[3]。如果有任何歧义,应使用空白。

样例代码

有几个槽的一个简单的类:

define class <point> (<object>)
  slot point-x :: <integer>,
    required-init-keyword: x:;
  slot point-y :: <integer>,
    required-init-keyword: y:;
end class <point>;

在约定上,类使用尖括号(即小于号和大于号)来命名,比如这个代码例子中的类名字<point>

end class <point>中,class<point>二者都是可选的。对所有end子句都是如此。例如,可以写end if或只写end来终止一个if语句。

同样的类,可以用极小化方式重写为:

define class <point> (<object>)
  slot point-x;
  slot point-y;
end;

槽现在都确定类型为<object>。槽必须被手动初始化。

在约定上,常量名字开始于$

define constant $pi :: <double-float> = 3.1415927d0;

阶乘函数:

define method factorial (n :: <integer>) => (n! :: <integer>)
  case
    n < 0     => error("Can't take factorial of negative integer: %d\n", n);
    n = 0     => 1;
    otherwise => n * factorial(n - 1);
  end
end;

这里的n!<integer>就是正常的标识符。

这里没有显式的返回语句。一个方法或函数的结果是最后求值的那个表达式。除掉在返回位置上的表达式后面的分号是常见的风格。

模块与命名空间

在很多面向对象语言中,类是封装和模块化的主要方式;每个类定义一个命名空间并控制哪些定义是在外部可见的。进一步的,在很多语言中类定义必须被用作一个整体的不可见单元。例如,使用String串接函数要求导入并编译全部的String

某些语言包括Dylan,还包括一个分立的显式的命名空间或模块系统,可以用更一般性的方式进行封装。

在Dylan中,编译单元和导入单元的概念是分开的,类对于二者都没有什么特殊可以言。“库”定义应当被一起编译和处理的项目,而“模块”定义一个命名空间。类可以一起放置在模块中,或分拆至其中,随编程者意愿。经常是一个类的完全定义不存在于一个单一的模块中,而是延展于进行选择性收集的多个模块至上。不同的程序可以有相同的类的不同的定义,并只包括它们所需要的。

例如,考虑支持String的一个附加库regex。在某些语言中,一个功能要被包括在字符串中,这个功能就必须被增加到String命名空间。随着这种事情不断发生,String类变得越来越大,而不需要使用regex的函数仍必须为增加了库大小付出代价。为此,这种附加件典型的放置在它们自己的命名空间和对象之中。这种方式的缺点是新函数不再是String的一部分;它转而被隔离在单独声明的它自己的函数集合之中。不再使用myString.parseWith(myPattern),从OO视角这是自然的组织方式,而是使用像 myPattern.parseString(myString)这样的东西,它在效果上反转了次序。

在Dylan之下,可以为相同代码定义很多接口,例如String串接方法可以给放置在String接口和concat接口二者之中,后者将不同的类中的不同的串接函数收集在一起。这常用于数学库中,这里的函数意图适用于广泛的不同对象类型。

接口构造的更实际用法是建造一个模块的公开和私有版本,在其他语言中这被包括为一个附带特征,并总是导致问题并增加语法。在Dylan之下,所有的函数调用可以简单的放置在“私有”或“开发”接口中,而把可公开访问的函数收集在Public接口之中。在JavaC++之下,一个对象的可见性是定义在代码中的,意味着要提供类似的变更,编程者将被强制的去完全重写定义,并且不能同时有两个版本。

在Dylan中以类似于大多数OO语言的风格,类描述了对象的slot(槽,数据成员,字段,ivar等)。 所有对槽的访问都要通过方法,就像Smalltalk那样。缺省的gettersetter方法基于槽名字而自动生成。对比于多数其他OO语言,可应用于类的其他方法经常定义于这个类的外部,因此在Dylan中类定义典型的只包括存储的定义。例如:

define class <window> (<view>)
  slot title :: <string> = "untitled", init-keyword: title:;
  slot position :: <point>, required-init-keyword: position:;
end class;

在这个例子中,定义了类<window><类名字>语法只是约定,使得类名字显得突出,尖括号只是这个类名字的一部分。与之相对比,在一些语言中,约定为大写类名字的首字母,或给名字前缀上C或T(举个例子)。<window>继承了一个单一类<view>,并包含二个槽:title持有这个窗口标题的字符串,和position持有这个窗口一角的X-Y点。在这个例子中,标题给出为缺省值,而位置还没有值。可选的init-keyword语法允许编程者在初始化这个类的对象时指定这个槽的初始值。

在语言比如C++或Java中,类还定义了它的接口。所以在这二种语言中,如果像上述案例中,类定义没有显式的对可见性的指令,则对数据成员和方法的访问都被当作是protected,意味着它们只能被子类使用。要使得无关代码使用这个窗口的实例,它们必须给声明为public

在Dylan中,这些槽的可见性规则不被当作这个代码的一部分,而是模块/接口系统的一部分。这增加了相当大的灵活性。例如,在早期开发中用的接口可以声明所有东西为public,而用在测试和部署中用的接口会加以限制。对于C++或Java,这种变更会需要改变原始码,所有人们就不做了,而在Dylan中,这是完全无关的概念。

尽管这个例子没有用到,Dylan还支持多重继承

方法和泛化函数

在Dylan中,方法不是固有的关联于任何特定的类;方法可以被认为存在于这个类之外。就像CLOS,Dylan是基于多分派(多方法)的,这里特定方法的调用是基于它的所有实际参数的类型来选择的。方法不需要在编译时间就知道,基于用户的偏好,所要求的函数可以是能获得到的也可以不能。

在Java之下,相同的方法被隔离在特定的类之中。要使用这个功能,编程者被强制去import这个类并显式的引用它来调用这个方法。如果这个类不可获得,或在编译时间未知,这个应用简单的不能编译。

在Dylan中,泛化函数表示零或多个类似的方法,用define method方式创建的所有方法自动的包含在同名的泛化函数之内。泛化函数也可以显式的用define generic声明,允许编程者精确控制可以增加哪种方法。代码被隔离于存储而位于泛化函数中。很多类在其中拥有它们自己的方法,因此在感官上就像多数其他OO语言一样。但是代码实际上位于泛化函数之中,意味着它们不附属于特定的类,并可以被任何人自然的调用。下例将类<window>的方法turn-blue并入同名的泛化函数之内:

define method turn-blue (w :: <window>)
  w.color := $blue;
end method;

这个定义类似于其他语言的定义,并有可能被封装到<window>类之中。注意:=这个setter调用,它是color-setter($blue, w)语法糖

泛化函数的自主利用可见于更泛化的例子之中。例如,在多数语言中的一个常见函数to-string,它返回这个对象的某种人类可读形式。比如说一个窗口可能返回它的标题和它在父窗口中的位置,而一个字符串将返回它自身。在Dylan中,这些方法可以给收集到叫作to-string的一个单一模块中,因而这个代码被从这个类自身的定义中移除。如果一个特定对象不支持to-string,可以简单的在to-string模块中增加上它。

扩展性

上述的整体概念可能让读者觉得很奇怪。处理一个窗口的to-string不定义在<window>之中。除非考虑到Dylan对to-string调用的处理方式,否则就可能变得没有意义。在多数语言中,在程序编译的时候,查找给<window>to-string并把它替代为到这个方法的一个指针(或多或少)。在Dylan中,这发生在程序初次运行的时候,运行时系统建造一个方法名字/形式参数细节的表格,并通过这个表格来动态的查找方法。这意味着一个特定方法可以位于任何地方,不只是在编译时间单元中。最后编程者得到了在放置其代码上的相当大的灵活性,只要合适,可以收集在它的类之中,也可以与相同功能的方法收集在泛化函数中。

这里隐含着编程者可以通过在单独文件中定义函数,来向现存的类增加功能。例如人们可能希望向所有<string>增加拼写检查,这在多数语言中将需要访问到字符串类的原始码,而这种基本类很少以原始码形式给出。在Dylan(和其他可扩展语言)中,拼写检查方法可以增加到spell-check模块中,通过define method构造定义它可以适用的所有的类。在这种情况下,实际功能可以定义在一个单一的泛化函数中,它接受一个字符串并返回错误。当spell-check模块被编译入程序的时候,所有字符串(和其他对象)都会得到这个增加的功能。

引用

  1. ^ https://opendylan.org/news/2022/11/28/new-release.html.
  2. ^ Stokel-Walker, Chris. Julia: The Goldilocks language. Increment. Stripe. [23 August 2020]. (原始内容存档于2020-11-09). 
  3. ^ 3.0 3.1 3.2 Andrew Shalit; David Moon; Orca Starbuck. The Dylan Reference Manual. Apple Press. Addison-Wesley. 11 September 1996 [2021-03-13]. ISBN 9780201442113. (原始内容存档于2021-05-30). 

外部链接