.NET

best365体育app-编程语言的发展趋势及未来方向(2):声明式编程与DSL

来源:代码作者:代码发布时间:2021-02-24

 

这是安德斯埃尔斯伯格在比利时科技日2010的开幕演讲。最近在博客里讲了很多语言,打算完全脱离场合听写安德斯的演讲。最后一部分,安德斯指出语言本身在过去的几十年里没有明显的发展,并对他眼中的编程语言的发展趋势进行了预测。在第二部分中,安德斯将解释声明式编程和DSL的概念,并用C#演示内部DSL的一种形式:LINQ。

本文所指的地址:

除非另有说明,所有的文字都是直接从安德斯的演讲中翻译过来,用我自己的口语习惯表达出来的。如果有必要,安德斯的口误和重复自然会在翻译中被忽略。为了方便理解,我也会截图视频中的关键部分,一些best365运动app演示会直接作为张文内容发布。

(听写开始,续)

让我们从声明性编程开始。

目前,我们在编写软件时使用了大量的命令式编程语言,如C#、Java或C等。这些语言的特点是,编写的best365运动应用程序不仅显示“什么”是你想做的,而且更多的best365运动应用程序显示实现细节,即“如何”完成工作。best365 sports app的这一部分,有时会掩盖了我们最初问题的解决方案。比如你会写For循环,if语句,A等于B,I加1,等等。在best365 sports应用程序中,显示了机器如何处理数据。首先,这种方式使得best365 sports app变得多余,执行best365 sports app的基础设施很难判断如何更智能地执行best365 sports app。当你写了best365 sports app这样的命令,然后把编译好的中间语言交给虚拟机执行时,虚拟机没有太多空间去影响best365 sports app的执行方式,只能老老实实的按照指令一个一个执行。比如我们现在很难并行执行程序,因为已经丢失了一些更高级的信息。这样我们只能给出best365运动app中的“如何”,不能体现“什么”的信息。

有许多方法可以将“什么”转换成更“声明性”的编程风格。只要我们能在best365 sports app中反映更多“什么”而不是“如何”的信息,执行环境就能更智能地适应当前的执行需求。比如可以决定投入多少CPU进行计算,你现在的硬件是什么,等等。

前面提到过,有两个重要的成果,一个是DSL(领域特定语言* * E),一个是函数式编程。

其实DSL也不是什么新鲜事。我们通常使用类似best365的运动应用,比如SQL、CSS、正则表达式,有的可能更侧重于一个方面,比如Mathematica、LOGO等等。这些语言的目标都是特定的领域,与之相反的是GPPL(通用编程语言)。

.tuchong.com/2443980/f/459866922.jpg">

对于DSL而言其实并没有一个明确的定义,在这里我也不打算为它下个定义,例如UML甚至根本没有特定的语法。不过我这里会谈一些我觉得比较重要的best365体育app

Martin Fowler提出DSL应该分为外部DSL及内部DSL两种,我认为这种划分方式还是比较有意义的。外部DSL是自我包含的语言,它们有自己特定语法、解析器和词法分析器等等,它往往是一种小型的编程语言,甚至不会像GPPL那样需要源文件。与之相对的则是内部DSL。内部DSL其实更像是种别称,它代表一类特别API及使用模式。这里我会给你们看一些示例。

这些是我们平时会遇到的一些外部DSL,如这张幻灯片上表现的XSLT,SQL或是Unix脚本。外部DSL的特点是,你在构建这种DSL时,其实扮演的是编程语言设计者的角色,这个工作并不会交给普通人去做。外部DSL一般会直接针对特定的领域设计,而不考虑其他best365体育app。James Gosling曾经说过这样的话,每个配置文件最终都会变成一门编程语言。你一开始可能只会用它表示一点点best365体育app,然后慢慢你便会想要一些规则,而这些规则则变成了表达式,可能你还会定义变量,进行条件判断等等。而最终它就变成了一种奇怪的编程语言,这样的情况屡见不鲜。

事实上,现在有一些公司也在关注DSL的开发。例如以前在微软工作的Charles Simonyi提出了Intentional Programming的概念,还有一个叫做JetBrains的公司提供一个叫做MPS(Meta Programming System)的产品。最近微软也提出了自己的Oslo项目,而在Eclipse世界里也有个叫做Xtext的best365体育app,所以其实在这方面现在也有不少人在尝试。

我在观察外部DSL时,往往会关注它的语法到底提供了多少空间,例如一种XML的方言,利用XML方言的好处在于有不少现成的工具可用,这样可以更快地定义自己的语法。

而内部DSL,正像我之前说的那样,它其实只是一系列特别的API及使用模式的别称。这里则是一些LINQ查询语句,Ruby on Rails以及jQuerybest365体育app。内部DSL的特点是,它其实只是一系列API,但是你可以“假装”它们一种DSL。内部DSL往往会利用一些“流畅化”的技巧,例如像这里的LINQ或jQuery那样把一些方法通过“点”连接起来。有些则利用了元编程的方式,如这里的Ruby on Rails就涉及到了一些元编程。这种DSL可以访问语言中的best365体育app或变量,以及利用如best365体育app补全,重构等母语言的所有特性。

现在我会花几分钟时间演示一下我所创建的DSL,也就是LINQ。我相信你们也已经用过不少LINQ了,不过这里我还是快速的展示一下我所表达的更为“声明式”的编程方式。

public class Product

{

public int ProductID { get; set; }

public string ProductName { get; set; }

public string CategoryName { get; set; }

public int UnitPrice { get; set; }

public static List

}

public partial class _Default : System.Web.UI.P**e

{

protected void P**e_Load(object sender, EventArgs e)

{

List

products = Product.GetProducts();

List

foreach (Product p in products)

{

if (p.UnitPrice > 20) result.Add(p);

}

GridView1.DataSource = result;

GridView1.DataBind();

}

}

这里有许多Product对象,那么现在我要筛选出所有单价大于20的那些, 再把他们显示在一个GridView中。传统的做法就是这样,我先得到所有的Product对象,然后foreach遍历每个对象,再判断每个对象的单价,最终把数据绑定到GridView里。运行这个程序……(打开页面)这就是就能得到结果。

好,那么现在我要做一些稍微复杂的事情。可能我不是要展示单价超过20的Product对象,而是要查看每个分类中究竟有多少个单价超过20的对象,然后根据数量进行排序。如果不用DSL完成这个工作,那么我可能会先定义一个对象来表示结果:

class Grouping

{

public string CategoryName { get; set; }

public int ProductCount { get; set; }

}

这是个表示分组的对象,用于保存分类的名称和产品数量。然后我们就会写一些十分丑陋的best365体育app:

Dictionary

foreach (Product p in products)

{

if (p.UnitPrice >= 20)

{

if (!groups.ContainsKey(p.CategoryName))

{

Grouping r = new Grouping();

r.CategoryName = p.CategoryName;

r.ProductCount = 0;

groups[p.CategoryName] = r;

}

groups[p.CategoryName].ProductCount++;

}

}

List

result.Sort(delegate(Grouping x, Grouping y)

{

return

x.ProductCount > y.ProductCount ? -1 :

x.ProductCount < y.ProductCount ? 1 :

0;

});

我先创建一个新的字典,用于保存分类名称到分组的对应关系。然后我遍历每个Product对象,对于每个单价大于20的对象,如果字典中还没有保存对应的分组则创建一个,然后将数量加一。然后为了排序,我调用Sort方法,于是我要提供一个委托作为排序方法,然后blablablabla……执行之后……(打开页面)我自然可以得到想要的结果。

但是,首先这些best365体育app写起来需要花费一些时间,很显然。然后仔细观察,你会发现这写best365体育app几乎都是在表示“How”,而“What”基本已经丢失了。假设我离开了,现在新来了一个程序员要维护这段best365体育app,他会需要一点时间才能完整理解这段best365体育app,因为他无法直接看清best365体育app的目标。

不过如果这里我们使用DSL,也就是LINQ,就像这样:

var result = products

.Where(p => p.UnitPrice >= 20)

.GroupBy(p => p.CategoryName)

.OrderByDescending(g => g.Count())

.Select(g => new { CategoryName = g.Key, ProductCount = g.Count() });

products……先调用Where……blablabla……再GroupBy等等。由于我们这里可以使用DSL来表示高阶的术语,用以体现我们想做的事情。于是这段best365体育app则更加关注于“What”而不是“How”。我这里不会明确地指示我想要过滤的方式,我也不会明确地说我要建立字典和分类,这样基础结构就可以聪明地,或者说更加聪明地去确定具体的执行方式。你可能比较容易想到我们可以并行地执行这段best365体育app,因为我没有显式地指定做事方式,我只是表示出我的意图。

我们打开页面……(打开页面)很显然我们得到了相同的结果。

这里比较有趣的是,内部DSL是如何设计进C#语法中的,为此我们为C# 3.0添加了一系列的特性,例如Lambda表达式,扩展方法,类型推断等等。这些特性统一起来之后,我们就可以设计出更为丰富的API,组合之后便成为一种内部DSL,就像这里的LINQ查询语言。

除了使用API的形式之外,我们还可以这样做:

var result =

from p in products

where p.UnitPrice >= 20

group p by p.CategoryName into g

orderby g.Count() descending

select new { CategoryName = g.Key, ProductCount = g.Count() };

编译器会简单地将这种形式转化为前一种形式。不过,这里我认为有意思的地方在于,你完全可以创建一门和领域编程语言完全无关的语法,然后等这种语法和API变得流行且丰富起来之后,再来创一种新的表现形式,就如这里的LINQ查询语法。我颇为中意这种语言设计的交流方式。

OK,现在我们回到下面的内容。

(未完待续)