您的位置 首页 应用

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

这是AndersHejlsberg(不用介绍这是谁了吧)在比利时TechDays2010所做的开场演讲。由于最近我在博客上关于语言的讨论比较多,出于应景,也打算将Anders的演讲完整地听写出来。

  这是Anders Hejlsberg(不必介绍这是谁了吧)在比利时TechDays 2010所做的开场讲演。由于最近我在博客上关于言语的评论比较多,出于应景,也计划将Anders的讲演完好地听写出来。在上一部分中,Anders指出言语自身在曩昔的数十年里并没有显着的开展,并给出了他眼中编程言语开展趋势的猜测。在现在的第2部分中,Anders将论述声明式编程的理念及DSL,并演示C#中一种内部DSL的办法:LINQ。

  假如没有特别阐明,一切的文字都直接翻译自Anders的讲演,并运用我自己的白话习气表达出来,关于Anders的口误及重复等状况,必要时在译文中天然也会进行疏忽。为了便利了解,我也会将视频中要害部分进行截图,而某些代码演示则会直接作为文章内容宣布。

  (听写开端,接上篇)

    

 

  这儿先从声明式(Declarative)编程谈起。

    

 

  现在咱们在编写软件时很多运用的是指令式(Imperative)编程言语,例如C#,Java或是C++等等。这些言语的特征在于,写出的代码除了表现出“什么(What)”是你想做的作业之外,更多的代码则表现出完结的细节,也便是“怎么(How)”完结作业。这部分代码有时候多到掩盖了咱们本来问题的解决方案。比方,你会在代码里写for循环,if句子,a等于b,i加一等等,这表现出机器是怎么处理数据。首要,这种做法让代码变得冗余,并且它也很难让履行代码的根底设施更聪明地判别该怎么去履行代码。当你写出这样的指令是代码,然后把编译后的中心言语交给虚拟机去履行,此刻虚拟机并没有多少空间能够影响代码的履行办法,它只能依据指令一条一条老老实实地去履行。例如,咱们现在想要并行地履行程序就很困难了,由于更高层次的一些信息现已丢掉了。这样,咱们只能在代码里给出“How”,而不能表现出“What”的信息。

  有多种办法能够将“What”转化为更为“声明式”的编程风格,咱们只需能够在代码中表现出更多“What”,而不是“How”的信息,这样履行环境便能够愈加聪明地去习惯当时的履行要求。例如,它能够决议投入多少CPU进行核算,你的当时硬件是什么样的,等等。

    

 

  我之前提到过,现在有两种比较重要的效果,一是DSL(Domain Specific Language,范畴特定言语),另一个则是函数式编程。

  其实DSL不是什么新鲜的玩意儿,咱们平常一向在用相似的东西,比方,SQL,CSS,正则表达式,有的或许愈加专心于一个方面,例如Mathematica,LOGO等等。这些言语的方针都是特定的范畴,与之相对的则是GPPL(General Purpose Programming Language,通用目的编程言语)。

    

 

  关于DSL而言其实并没有一个清晰的界说,在这儿我也不计划为它下个界说,例如UML乃至底子没有特定的语法。不过我这儿谈判一些我觉得比较重要的东西。

    

 

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

    

 

  这些是咱们平常会遇到的一些外部DSL,如这张幻灯片上表现的XSLT,SQL或是Unix脚本。外部DSL的特点是,你在构建这种DSL时,其实扮演的是编程言语规划者的人物,这个作业并不会交给普通人去做。外部DSL一般会直接针对特定的范畴规划,而不考虑其他东西。James Gosling从前说过这样的话,每个配置文件终究都会变成一门编程言语。你一开端或许只会用它表明一点点东西,然后渐渐你便会想要一些规矩,而这些规矩则变成了表达式,或许你还会界说变量,进行条件判别等等。而终究它就变成了一种古怪的编程言语,这样的状况层出不穷。

  事实上,现在有一些公司也在重视DSL的开发。例如曾经在微软作业的Charles Simonyi提出了Intentional Programming的概念,还有一个叫做JetBrains的公司供给一个叫做MPS(Meta Programming System)的产品。最近微软也提出了自己的Oslo项目,而在Eclipse国际里也有个叫做Xtext的东西,所以其实在这方面现在也有不少人在测验。

  我在调查外部DSL时,往往会重视它的语法终究供给了多少空间,例如一种XML的方言,运用XML方言的优点在于有不少现成的东西可用,这样能够更快地界说自己的语法。

    

 

  而内部DSL,正像我之前说的那样,它其实仅仅一系列特别的API及运用方法的别称。这儿则是一些LINQ查询句子,Ruby on Rails以及jQuery代码。内部DSL的特点是,它其实仅仅一系列API,可是你能够“伪装”它们一种DSL。内部DSL往往会运用一些“流通化”的技巧,例如像这儿的LINQ或jQuery那样把一些办法经过“点”连接起来。有些则运用了元编程的办法,如这儿的Ruby on Rails就触及到了一些元编程。这种DSL能够拜访言语中的代码或变量,以及运用如代码补全,重构等母言语的一切特性。

    

 

  现在我会花几分钟时刻演示一下我所创立的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 GetProducts() { /* … */ }

  }

  public partial class _Default : System.Web.UI.Page

  {

  protected void Page_Load(object sender, EventArgs e)

  {

  List products = Product.GetProducts();

  List result = new 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; }

  }

  这是个表明分组的方针,用于保存分类的称号和产品数量。然后咱们就会写一些非常丑恶的代码:

  Dictionary groups = new 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 = new List(groups.Values);

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

  {

  return

  x.ProductCount > y.ProductCount ? -1 :

  x.ProductCount < y.ProductCount ? 1 :

  0;

  });

  我先创立一个新的字典,用于保存分类称号到分组的对应联系。然后我遍历每个Product方针,关于每个单价大于20的方针,假如字典中还没有保存对应的分组则创立一个,然后将数量加一。然后为了排序,我调用Sort办法,所以我要供给一个托付作为排序办法,然后blablablabla……履行之后……(翻开页面)我天然能够得到想要的成果。

  可是,首要这些代码写起来需求花费一些时刻,很显然。然后仔细调查,你会发现这写代码简直都是在表明“How”,而“What”根本现已丢掉了。假定我离开了,现在新来了一个程序员要保护这段代码,他会需求一点时刻才干完好了解这段代码,由于他无法直接看清代码的方针。

  不过假如这儿咱们运用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来表明高阶的术语,用以表现咱们想做的作业。所以这段代码则愈加重视于“What”而不是“How”。我这儿不会清晰地指示我想要过滤的办法,我也不会清晰地说我要树立字典和分类,这样根底结构就能够聪明地,或者说愈加聪明地去确认详细的履行办法。你或许比较简略想到咱们能够并行地履行这段代码,由于我没有显式地指定干事办法,我仅仅表明出我的目的。

  咱们翻开页面……(翻开页面)很显然咱们得到了相同的成果。

  这儿比较风趣的是,内部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,现在咱们回到下面的内容。

  (未完待续)

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/yingyong/114990.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部