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

 

这里先从声明式(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<Product> GetProducts() { /* ... */ }
}public partial class _Default : System.Web.UI.Page{    protected void Page_Load(object sender, EventArgs e){        List<Product> products = Product.GetProducts();        List<Product> result = new List<Product>();        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<string, Grouping> groups = new Dictionary<string, Grouping>();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<Grouping> result = new List<Grouping>(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() descendingselect new { CategoryName = g.Key, ProductCount = g.Count() };

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

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

原文链接:http://blog.zhaojie.me/2010/04/trends-and-future-directions-in-programming-languages-by-anders-2-declarative-programming-and-dsl.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/328237.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HtmlParser提取网页中的纯文本信息

转载自 HtmlParser提取网页中的纯文本信息HTMLParser 一个解析web页面的开源类库。 准备学习下搜索方面的技术&#xff0c;就学习了些网络爬虫的知识。最近一直在一个点上困惑&#xff0c;如何提取一个网页上的纯文本信息。要使用正则表达式的话呢&#xff0c;需要考…

编程语言的发展趋势及未来方向(1):历史回顾及趋势概述

大家好&#xff0c;我是Anders Hejlsberg&#xff0c;现在是微软的Technical Fellow&#xff0c;担任C#编程语言的首席架构师&#xff0c;也参与并领导.NET Framework以及各种语言的开发。我现在打算谈一下……实际上是我脑海中一些影响未来5到10年编程语言设计的内容。比如C#或…

调用反射类的指定方法

package org.entity;import java.lang.reflect.Method;/*** 本案例演示如何通过反射将字符串转换为类* */ public class Test2_2 {public static void main(String[] args) {String user "org.entity.User";//字符串是该类的全限定名try {Class clzz Class.forName…

springboot创建项目 编写dao serviece 和controller 持久层用mybatis

11 刷新一下pom 万一没有引入进去 jpa和mybatis选择哪个&#xff1f; 1.看领导要求 2.都会最好 多学点是没错的

android查看wifi是否双频,Android判断wifi是5G还是2.4G

我一开始看这帖子&#xff0c;找不到答案&#xff0c;为了后来的人&#xff0c;我来回复吧。WifiManager wifiManager (WifiManager) getSystemService(Context.WIFI_SERVICE);WifiInfo wifiInfo wifiManager.getConnectionInfo();根据wifiInfo.getFrequency()可以判断是不是…

java过滤html标签获取纯文本信息

转载自 java过滤html标签获取纯文本信息 package com.lyt.base.util;import java.util.regex.Pattern;public class FilterHtmlUtil {public static String Html2Text(String inputString){String htmlStr inputString; //含html标签的字符串String textStr "";java…

综述:编程语言的发展趋势及未来方向

程序设计离不开编程语言&#xff0c;但是编程语言在国内的大环境中似乎一直是个二等公民。国内的计算机教育和工程培训&#xff0c;似乎一直在宣传“语言不重要&#xff0c;重要的是思想”&#xff0c;“语言一通百通”等观点&#xff0c;甚至在许多人眼中“语言的讨论”完全是…

IntelliJ IDEA: 无法创建Java Class文件

IntelliJ IDEA: 无法创建Java Class文件 2018年06月21日 22:49:44 dandelion9508 阅读数 11864 今天打开项目时&#xff0c;发现右击新建不了java.class文件&#xff0c;于是尝试了以下方法&#xff1a; &#xff08;1&#xff09;选择 File——>Project Structure——>…

iphone android互传文件夹,安卓和苹果手机怎么互传文件_安卓与苹果手机之间互传文件的方法教程_3DM手游...

一直以来&#xff0c;很多的app都是分为安卓和IOS两个版本&#xff0c;在使用上&#xff0c;两个平台之间很多功能都不能跨平台使用。比如大家在玩游戏时充值的东西&#xff0c;在苹果充值之后&#xff0c;同一个账号转到安卓手机上玩&#xff0c;那些东西都是不能迁移进来的。…

jsoup怎么获取两个标签之间的text?

转载自 jsoup怎么获取两个标签之间的text? 这是开发者工具解析到的一个论坛页面结构。。可以看到每一个a标签和br标签之间总会夹着一个text,,也就是密码文本。而我根据网上的API教程&#xff1a;siblingA ~ siblingX: 查找A元素之前的同级X元素&#xff0c;比如&#xff1a…

编程语言的发展趋势及未来方向(7):总结

OK&#xff0c;我想现在已经讲的差不多了&#xff0c;我来做个总结吧。 在我看来&#xff0c;对于编程语言来说&#xff0c;现在出现了许多有趣的东西&#xff0c;也是令人激动的时刻。在过去&#xff0c;大约1995-2005年&#xff0c;的确可以说是一个有些特别的编程语言的黄金…

通过反射获取方法返回的类型

package org.entity;import java.lang.reflect.Method; import java.lang.reflect.Type;/*** 本案例演示如何通过反射将字符串转换为类* */ public class Test3 {public static void main(String[] args) {String user "org.entity.User";//字符串是该类的全限定名t…

android 按键消息,在android中模拟键盘消息(shell命令的方法)

已找到解决方案[code]use: sendeventcommand format: sendeventdevice type code value[command] [device] [type] [code] [value]sendevent/dev/input/event0 1 229 1/dev/input/event0 is the device to send it to[type] 1 is unknow for me ( maybe code for physical butt…

强行覆盖远程的gitlab 出错

需要删除原来的.git 文件 重新生成一次 强行覆盖远程的gitlab 出错 我这里出错的原因是 因为我本地一个readme.md和远程的README.MD文本内容不同 我将本地的readme.md 删除后重新操作就对了 1.删除原来的.git文件 AdministratorUSER-20170819XG MINGW64 /e/unicomWork…

使用 jsoup 对 HTML 文档进行解析和操作

转载自 使用 jsoup 对 HTML 文档进行解析和操作jsoup 简介 Java 程序在解析 HTML 文档时&#xff0c;相信大家都接触过 htmlparser 这个开源项目&#xff0c;我曾经在 IBM DW 上发表过两篇关于 htmlparser 的文章&#xff0c;分别是&#xff1a;从 HTML 中攫取你所需的信息和…

编程语言的发展趋势及未来方向(6):并发

好&#xff0c;最后我想谈的内容是“并发”。 听说过摩尔定律的请举手……几乎是所有人。那么多少人听说了摩尔定律已经结束了呢&#xff1f;嗯&#xff0c;还是有很多人。我有好消息&#xff0c;也有坏消息。我认为摩尔定律并没有停止。摩尔定律说的是&#xff1a;可以在集成电…

GitLab创建项目 命令上传代码

注册哪里要验证点击图片 这里需要外网 有可能 右键gitbash 首次将本地创建项目推送远程代码仓库 touch readme.md 创建readme文件 git init 初始化git git status 查看修改状态 git add . 添加至本地仓库管理 git status 检查是否添加成功 git commit -am fir…

Linux ss 热点,在Linux系统下的ss命令(socket statistics)各种使用示例

本文演示在Linux操作系统下的ss命令(socket statistics)的各种使用示例。ss(套接字统计信息)是一个命令行工具&#xff0c;用于监视套接字连接并显示Linux系统的套接字统计信息。它可以显示PACKET套接字、TCP套接字、UDP套接字、DCCP套接字、RAW套接字、Unix域套接字等的统计信…

Jsoup代码解读之七-实现一个CSS Selector

转载自 Jsoup代码解读之七-实现一个CSS Selector当当当&#xff01;终于来到了Jsoup的特色&#xff1a;CSS Selector部分。selector也是我写的爬虫框架webmagic开发的一个重点。附上一张street fighter的图&#xff0c;希望以后webmagic也能挑战Jsoup! select机制 Jsoup的se…

http1.1协议

HTTP/1.1协议 2018年01月04日 17:27:35 上杉绘梨衣- 阅读数 245 HTTP是hypertext transfer protocol(超文本传输协议)的简写&#xff0c;它是TCP/IP协议的一个应用层协议&#xff0c;用于定义WEB浏览器与WEB服务器之间数据交换的过程。 1.Http1.0和1.1 Http1.0:请求一次&…