acm-header
登录

ACM通信

实践

为什么LINQ很重要:保证云可组合性


为什么LINQ很重要

图片来源:Alex Williamson

回到顶部

可组合性是组件设计的一个方面,它解决了以几乎任意的配置自由选择和组合通用组件,以支持各种各样的应用程序,甚至是组件设计人员没有预料到的应用程序。例如,电子元件是常规设计的,因此开关、连接和负载几乎可以按任何顺序配置,以满足各种各样的应用。组件设计人员不需要知道应用程序的细节,组件用户也不受设计人员任意选择的过度限制。同样,在机械工程中,许多应用程序可以完全由通用的支架、铰链、紧固件等来构建,在一开始设计时,可以根据尺寸、螺距、材料强度等的标准化,以任何顺序和布局进行配置。

在本文中,我们使用语言集成查询(LINQ)作为可组合性的指导示例。LINQ是专为可组合而设计的高阶运算符规范。该规范广泛适用于任何符合“集合”松散定义的东西,从内存中的对象到异步数据流,再到分布在云中的资源。通过这样的设计,开发人员通过以各种顺序将转换和过滤器链接在一起以及通过嵌套链(即通过构建运算符的表达式树)来增加复杂性。

在分布式系统的各个层之间编码和传输这样的操作符树有许多具体的好处,最明显的是:

  • 将过滤器注入到更靠近数据和流生产者的地方,可以节省带宽,避免将不需要的数据传输回消费者。
  • 在云计算中执行计算的计算效率,在云计算中可用的计算能力比在客户机中要大得多。
  • 可编程性,可以向数据消费者提供通用的转换和筛选服务,避免在数据生产者站点上对查询和数据模型进行预审的需要。

本文还介绍了通过REST(具像状态传输)将程序的可组合性提升到云中,以及程序表达式和uri中的资源规范之间的映射;它还解决了设计可组合性的微妙风险:看似不重要的、任意的选择可能导致设计从根本上不可组合。

回到顶部

Meta-Design原则

可组合性设计则不然仅仅是分离接口和实现。它还意味着接口上的某种一致性:元设计原则.如果每个插座和插头的设计都是定制的、特殊的形状,即使这种设计足以携带所需的电流,也不会有多大好处。正是因为插座和插头的形状是标准化的,所以我们可以灵活地将它们组合成几乎任意的配置。

物理工程学科(例如,电气和机械)在可组合性设计方面有着悠久的历史,因为其好处是如此明显和压倒性,但在软件工程中,这种好处也变得越来越明显。软件设计人员早就接受了黑箱:需要在接口中封装实现细节,公开最小的、精确的、完整的契约。设计人员现在开始抽象接口本身,并意识到可组合性的组合好处。

纯函数式编程的规程,以编程语言Haskell (http://www.haskell.org)及其前身米兰达(http://en.wikipedia.org/wiki/Miranda_programming_language),将数学函数的绝对可组合性保证引入了编程领域。它们的影响与命令式和面向对象编程传统相结合,代表了最近的、深刻的和重要的巩固:一个大概念,函数,将几个较小的概念作为用例。关系、对象、状态和流都具有函数风格的自然表示。这种风格越来越受欢迎的原因有以下几点:

回到顶部

LINQ是一种多域可组合模式

通过指定一组可组合的高阶函数sqo(标准查询操作符;http://en.wikipedia.org/wiki/Language_Integrated_Query#Standard_Query_Operators).这种特定的设计概括了集合的松散抽象,并涵盖了各种各样的领域:

  • 内存中的数据结构(例如,列表、树、图、队列)。
  • 数据库中的表(即使有主键和外键约束)。
  • 异步数据流(RX;http://msdn.microsoft.com/en-us/data/gg577609).
  • uri中斜杠分隔的术语;因此,云中的资源。
  • 信号处理原语(卷积和傅里叶变换)。
  • codedom(代码文档对象模型);表达式树;ast(抽象语法树),即编程语言本身。
  • HTML、XML和JSON (JavaScript对象表示法)文档。
  • 延续、异常、替代等等。

这种设计为所有这些域带来了相同的可组合操作符的完整集合。你可以说LINQ带来了可组合性本身到这些领域。如果实现了sqo,那么就保证了可组合性。LINQ不是第一个,当然也不是唯一的方法,但它足以支持本文中的其他观察,即:

  • 将程序中的可组合性提升到云中的可组合性。
  • 说明未来可组合设计面临的危险。

回到顶部

从程序到云

因此,LINQ的sqo可以作为我们自身可组合性元设计原则的范例。正如通常在程序中实现的那样,sqo以各种方式将函数应用于集合,进行识别作为程序中可组合组件之间的自然交互约定

从程序内的可组合性跳到云中的可组合性,您必须在上面“实现”sqo云中可组合组件之间的自然交互约定,不管那是什么。这只是LINQ的另一个领域,被视为任何满足LINQ集合的松散抽象的可组合运算符的规范。云只是uri指定的资源的松散集合。

什么是“云中可组合组件之间的自然交互约定”?也许当今最重要的交互约定是REST (http://en.wikipedia.org/wiki/REST),其中客户端和服务器通过HTTP交互,通过不透明的uri表示资源.REST在uri上不指定任何语义,只指定用斜杠分隔的术语的非常简单的后缀语法。OData (http://www.odata.org/)使用URI编码和REST为云提供数据框架,特别是通过授权客户端将所需计算或查询的结果指定为资源。让我们在此添加以下想法:

  • 激进的可组合性.用户通过以任意顺序连接SQO表达式序列来增加复杂性,而不是像OData那样在假定的数据模型上使用丰富的语法。
  • 双向映射.这是程序中的表达式链和uri中的资源规范之间的映射。给定这样一个映射,您可以对云交互中的程序和资源中的表达式进行推理,就好像它们是相同的一样。
  • 注入能力.表达式链的接收端可以在多个通信中保持持续操作,以节省带宽。比方说,客户端请求一串“我附近的中国餐馆”,可以一次性向服务器注入一个过滤器,然后避免通过未来的推送通知序列向客户端发送其他不相关的业务记录。服务器被来自客户端的过于频繁的位置更新所淹没,可能会向客户端注入一个过滤器,上面写着“仅每10次更新一次”,客户端在执行物理通信之前应用这个过滤器。
  • 广泛的适用性.相同的可组合设计适用于静态数据资源和依赖时间的异步数据流(以及其他域)。

此时,假设程序中的可组合操作符链表达式与云中可组合资源规范之间具有自然的对应关系。下面将介绍一种将通信机械化的具体方法,但是通信本身允许您以相同的方式推理哪些在程序中工作,哪些在云中工作。

回到顶部

在URI语法中嵌入LINQ

LINQ的典型实现嵌入到支持高阶函数的编程语言中。然而,采用LINQ只意味着接受sqo的规范。作为一种设计,LINQ根本不需要一种普通的编程语言。注意,可以将嵌套的操作符树转换为纯后缀运营商;编程语言中由点分隔的操作符链在形式上与URI中由斜杠分隔的术语链同构,您可以在URI语法中嵌入任何LINQ链,并在RESTful Web服务中使用HTTP作为“表达式嵌入语言”。

例如,想象一下在图1,在伪c#中,它表示最近购物的客户的电话号码:

它说:“留住顾客在哪里那么,现在的日期和时间与最后一次购买的日期和时间之间的差异小于365天选择最终收集的客户中每个客户的主要电话联系号码。”在哪里而且选择都是LINQ sqo。每一个都有两个参数——一个数据收集参数在每个点的左边,一个高阶函数在圆括号内——每个都产生一个新的集合。这是它们的链可组合性的本质:每个SQO表达式表示一个数据集合,准备按照从左到右的顺序将其点入链下的下一个SQO。

的高阶函数参数在哪里SQO是一个写成lambda表达式的谓词:

ins01.gif

的函数客户它执行函数体表达式指定的计算,并生成表达式的结果值。”因为这个特定的lambda表达式表示一个谓词,所以它应该产生一个布尔值。在JavaScript中,您可以编写完全相同的内容

ins02.gif

的高阶函数参数选择SQO是另一个lambda表达式,它只从客户记录中选择电话号码字段,但它可以进行任意计算。

大多数语言或库都调用它选择但是LINQ的设计者选择了这个名字选择以吸引SQL开发人员。在一些理论讨论中,也称为算符项目。让我们继续选择现在。

因为表达式链不包含嵌套运算符,所以它已经在浅的后缀形式,如果我们认为lambda表达式是原子的:

ins03.gif

只需在协议和域前面加上一个前缀,用斜线替换点:

ins04.gif

然后对lambda进行url编码,最终得到如下结果:

ins05.gif

因此,您在URI中获得了整个表达式的表示。基于rest的服务器可以在安全沙箱中解释这样的表达式,并提供非常通用的查询服务,而不需要内置的、预先屏蔽的查询。

如果您不能或不希望将lambda视为原子的,那么您可以一直到深的后缀形式,将整个AST以后缀形式写入,并将其编码到URI中。

你可以分两个阶段做到这一点:首先保持lambdas内爆,这样你就可以看到深度前缀和浅前缀之间的区别;然后引爆炸弹。首先以前缀形式重写查询,将查询运算符圆点左侧的所有内容拖到第一个位置运算符圆括号内的右侧。这句话把这个短语翻了个底朝天,就像毛衣的袖子一样,如下所示:

ins06.gif

现在,很容易看出,每一级都是一个二进制运算符,第一个参数槽中有一个数据集合,第二个槽中有一个lambda。这对应于中的AST图2.在不爆炸lambda的情况下,从左到右、深度优先遍历产生一个深度后缀形式,lambda仍然内爆:

ins07.gif

它看起来类似于浅后缀形式,只是带有sqopost-lambda可以这么说。sqo出现在lambda之后,而不是在它们的括号内驻留lambda。在深后缀形式中,有从来没有任何括号,这就是重点。这应该会让您想起PostScript或Forth或RPN(反向波兰符号)计算器,因为它们都是深度后缀(http://en.wikipedia.org/wiki/Concatenative_programming_language).

现在,将lambda扩展到所示的AST中图3.如果没有括号,它在URI中的RESTful编码很简单:

ins08.gif

其余的圆点表示属性访问器调用、系统调用或lambda变量声明,而不是SQO调用。URI编码将SQO调用前的所有点替换为斜线。

注意,在后缀中编码lambda还有其他选项。此示例使用了一个表示,其中变量在声明之前出现。这需要一个执行模型,该模型将变量和相关操作以符号的方式保存在堆栈上,稍后在声明变量的lambda项到达时进行解析。其他有能力的选择比比皆是。例如,PostScript和Factor在数组中编码lambda表达式,其中变量声明位于函数体之前。这相当于在其他后缀语言中嵌入了作弊者前缀符号。

我们已经脱离了人类可读语法的领域(除了Forth的爱好者),但已经发现了ast的URI编码,对于机器读写来说是微不足道的。即使对于嵌套LINQ,我们可能也不需要走这么远。圆括号计数可以很容易地管理嵌套,同时保持人类的可读性。

对于一个带有嵌套sqo的示例,想象表达式在图4同样是在伪c#中,它表示来自客户列表的高价值订单的行项目。

回到顶部

可组合性设计中的危险

尽管LINQ是可组合设计的一个很好的例子,它在内存中的对象上和在云中的资源上工作得一样好,但它肯定不是唯一的一个。然而,拥抱激进的可组合性的设计师将面临一些危险。如果他们在设计中出现了一些轻微的错误,那么用户就不能再在设计中指定他们需要什么。

在一个典型的例子中,假设您需要一个来自大客户的所有订单的集合。你的图书馆提供了一种过滤低消费客户的方法,它提供了一种可组合的为每个客户获取订单的方法。到目前为止,一切顺利。在伪c#中,您可以尝试中所示的示例图5

然而,这不会产生所需的订单集合;相反,它生成订单集合的集合—每个客户的一个内部订单集合。您需要平展顶级集合。您必须离开库并编写自定义代码。

在云设置中,离开库意味着为每个未发现的情况增加自定义代码,限制了仅从可组合的构建块构建新表达式的能力。任何有表达式的地方就像这里的这个,你必须做一些带外的事情来去除多余的结构。

在程序设置中,离开库意味着在系统的不同位置插入脆弱的变通代码的风险。也许程序员知道这一点订单是数组,因此写入块复制操作来平展它们。后来,当订单将实现从数组更改为gzip XML,此代码意外失败。


令人惊讶的是,像LINQ的SQOs这样的操作符的单一规范既适用于异步数据流,也适用于内存中的对象。


但是,如果图书馆提供了所需的SelectMany首先,程序员应该编写如下所示的代码图6,它与前面的代码只有一个区别(下划线)。

回到顶部

可组合性的广度和深度

令人惊讶的是,像LINQ的SQOs这样的操作符的单一规范既适用于异步数据流,也适用于内存中的对象但确实如此.用户不仅可以对提取的数据资源指定任意转换,还可以对使用同一组sqo的推送异步数据流指定任意转换。在这两种情况下,表达式组件都可以透明地注入到上游的数据生成器机器中(无论是客户机还是服务器),在源头转换和过滤数据,以节省带宽并减少“闲聊”。

然而,在一种自我引用的笑话中,处理程序中函数的同一组sqo也处理分布在云中的资源。因此,有一个相同SQO设计的八组应用程序:

  • sqo操作程序内的数据或云上的数据。
  • 分布在空间上的数据或者与分布在时间上的数据相结合。
  • 在数据生产者站点执行的转换和过滤器与在数据消费者站点执行的转换和过滤器相比,要考虑到诸如带宽、延迟和安全性等问题。

LINQ再次借鉴了Haskell的基本概念,特别是它的初始化库Prelude。这项技术的精髓是集合的松散抽象例如(不要打哈欠),顾客,订单,物品。每个客户有多个订单,每个订单有多个项目。从模式的角度来看,这些客户是在内存中还是已交付并不重要n或通过延续线程,或在web服务调用可访问的脱机文档存储中。重要的是在您的系统中有一个抽象来表示它们,在那里您想要操作它们。所需的可组合运算符分为以下几类(示例来自LINQ和Haskell Prelude):

  • 插入元素放入集合中(例如,new List(…)/ return)。这就是你进入收藏的方式。例如,将客户记录转换为单记录表;创建一个单例列表(包含单个客户对象)。
  • 变换元素并生成一个新的集合(例如,.选择/映射).例如:从客户列表中生成客户的主要电话号码列表,每个客户一个电话号码。
  • 过滤器基于谓词(例如,其中/ filter).例如,列出有大订单的客户名单;一般来说,这些东西的数量会比原始集合少。
  • 扩大将一个集合中的元素放入新的子集合中,并组合或连接新集合(例如,.SelectMany / concatMap, bind, >>=).例如,获得客户的所有一级朋友;获得所有客户的所有订单;从所有订单中获取所有行项目。
  • 元素并生成一个依赖于所有元素的结果(例如,。聚合/折叠;而且拿给而且.Skip / take, drop也属于这一类)。这就是您退出集合的方式(从技术上讲,这是一个共一元运算符EXTRACT)。例如,计算所有顾客的平均消费。

这些都是基本的。如果集合抽象在每一个类别中支持适当的原语,那么可组合性就得到了保证。

回到顶部

如何毁掉一个图书馆

你不需要写完全LINQ sqo;如果你了解基本原理,就会有很大的自由。然而,一个严重的问题是忽略了扩展类别,设计师经常这样做,因为它不符合某些领域熟悉的地图/滤镜/折叠咒语。扩展类别是必要的;事实上,它是最重要的。没有它,库就不能表示最一般的集合类型,因此就不能扩展到新的设置,例如异步数据流或云中的资源。使用它(以及INSERT),可以精确模拟所有其他类别(有关更多信息,请参阅http://channel9.msdn.com/Shows/Going+De-Smet-MinLINQ-The-Essence-of-LINQ).

附录中的图表图7演示运算符的类别。这些图一起显示了为什么EXPAND类别是最重要的。它是FILTER的孪生兄弟,尽管它更通用。

另一种破坏库的方法是把参数的顺序搞错。要链式操作符,更不用说将它们嵌入uri和REST中了,您总是希望集合参数位于第一个位置。传统上,Lisp的方言把函数首先是论证,至少是地图,因为它使表达式读为“将这个函数映射到那个集合上。”具有Lisp背景的库设计人员可能只会条件反射地编写地图这样的。这个语法上的小错误意味着地图一个省,选择,破坏了模式,除了在开始时,不能组成点状链。

回到顶部

结论

LINQ的模式是无懈可击的,无处不在的,应用于一个惊人广泛的软件领域,如异步流、有状态计算、I/O、异常、替代——任何符合“事物集合”的松散抽象的东西。模式总是可组合的表达式树,其中相关操作符在嵌套序列中相互跟随。它可以用普通编程语言语法进行编码,也可以用uri等纯后缀符号进行编码。

因为云符合“事物集合”的松散定义,所以LINQ的可组合转换模式涵盖了云中的分布式资源以及程序中的本地可寻址资源。将子表达式打包并将它们注入到更靠近数据生产站点的地方,可以使您在RESTful Web服务中获得带宽、通信量和安全性方面的好处。

ACM队列的q戳相关文章
queue.acm.org

LINQ的世界
埃里克·梅耶尔
http://queue.acm.org/detail.cfm?id=2024658

确保云中的弹性
达斯汀•欧文斯
http://queue.acm.org/detail.cfm?id=1794516

为什么云计算永远不会免费
戴夫Durkee
http://queue.acm.org/detail.cfm?id=1772130

回到顶部

作者

布莱恩·贝克曼目前在微软的Bing on Maps和Signals工作。自1992年以来,他在微软担任过许多职位,从Crypto (SET)到Biztalk,再到函数式编程的研究。1984年至1989年,他在加州理工学院的超级立方体上编写了第一个版本的时间扭曲操作系统。

回到顶部

数据

F1图1。包括滤波器和投影的LINQ表达式。

F2图2。AST显示内爆lambda。

F3图3。AST与爆炸的lambda。

F4图4。带有嵌套运算符的LINQ表达式示例。

F5图5。产生多余嵌套的示例。

F6图6。的例子SelectMany这避免了不必要的嵌套。

F7图7。可组合运算符的类别。

回到顶部


©2012 acm 0001-0782/12/0400 $10.00

允许为个人或课堂使用部分或全部作品制作数字或硬拷贝,但不得为盈利或商业利益而复制或分发,且副本在首页上附有本通知和完整的引用。除ACM外,本作品的其他组件的版权必须受到尊重。允许有信用的文摘。以其他方式复制、重新发布、在服务器上发布或重新分发到列表,都需要事先获得特定的许可和/或费用。请求发布的权限permissions@acm.org传真(212)869-0481。

数字图书馆是由计算机协会出版的。版权所有©2012 ACM, Inc.


评论


CACM管理员

以下信件发表在2012年8月出版的《致编辑的信》(//www.eqigeno.com/magazines/2012/8/153804)上。
——CACM管理员

我完全同意Brian Beckman在他的文章《为什么LINQ很重要:云可组合性保证》(2012年4月)中概述的可组合性好处,因为我有使用可组合性原则在专有网络中为移动自组织路由器设计和实现消息传播机制的经验。在其中,路由器的消息传播功能来自一个可组合树中大约1500个节点的聚合,该组合树类似于本文中lambda树图的大型版本。但是,每个节点不是基于linq的,而是表示一个控制元素(如if/else、for-loop和布尔操作节点)以及直接访问消息属性的节点。每个传入消息都遍历可组合树,控制节点根据消息属性(例如消息类型、时间戳和发送者位置)将其引导到相关的分支,直到消息到达完成传播的处理节点。

由于在代码库中组装和维护一个1500节点的树是令人生畏的,解析器根据基于特定于领域的语言(DSL)的1300行路由规则规范组装树。a通过这个dsl组装的可组合树定义路由规则还提供了以下额外的好处:

独立验证节点。验证if/else、消息时间戳和其他节点可以独立完成;

为单元测试修改的路由规则。随着路由规则的成熟,它们的执行需要一个完整的实验室或现场配置环境,这使得测试新特性变得困难;DSL规范的本地副本的快速简化定义了绕过无关的实验室/领域约束的路由规则,同时专注于在开发人员桌面上测试的特性;

可扩展和健壮。新的路由规则可以添加到DSL规范中;通过定义新的节点类型,可以添加新的路由概念;并且可以在整体设计中加入新的技术;而且

可组合树记录的每个消息遍历。可组合树中的每个节点记录一个简短的单行语句,描述它正在做什么以及为什么消息选择特定的遍历路径;这些语句的聚合提供了一个行程表,描述了每个消息在可组合树中的遍历过程,用于确认或调试。

我对通过DSL定义组合树的经验非常肯定,我肯定会再次考虑使用该技术来解决范围有限但变化无限的问题。

吉姆Humelsine
海王星,新泽西


CACM管理员

以下公开信发表在2012年7月出版的《致编辑的信》(//www.eqigeno.com/magazines/2012/7/151227)上。
——CACM管理员

Brian Beckman在2012年4月发表的文章《LINQ的重要性:云可组合性的保证》(Why LINQ Matters: Cloud Composability Guaranteed)的唯一目的似乎是试图表明LINQ与流行词汇兼容,比如“云”和“RESTful Web服务”。虽然我同意Beckman的主要观点,即可组合性和设计是重要和有用的,但我反对本文的阐述有以下原因:

首先,它声称LINQ适用于图(以及其他东西)。如果一个图要被视为一个集合,那么它首先必须被线性化(例如通过拓扑排序),但是循环呢?此外,查询的结果与所选择的线性化无关,这一点一点也不明显。由于没有具体的例子,这篇文章在说明卷积、延续和异常可以被视为LINQ运算符可用的序列时显得相当缺乏说服力。

其次,本文主要致力于说服读者,LINQ查询可以表示为可在postder中遍历并可在统一资源标识符(URI)中编码的抽象语法树(AST),这是一种无聊的、众所周知的事实。作为一名工程师实践者,我反对在uri中编码查询的想法,但出于智力练习的考虑,我接受了它。

第三,它对号称对可组合性至关重要的操作符EXPAND类别的描述不足。例如,对于SelectMany操作符,我必须从附近的文本中推断出SelectMany应该做什么,但是本文的其他“示例”(一句话和图7)对我理解这个类别应该做什么没有任何帮助。为什么“来自所有客户的所有订单”需要扁平化/选择多个,而只涉及一个收集订单?

最后,我不同意参数的顺序会破坏运算符可组合性的说法。除非Beckman希望开发人员通过粘贴文本片段(手动?)在uri中对查询进行编码,否则机器可能会从参数顺序“错误”的表示中重构AST,并将其作为构造另一个查询的AST的起点。尚不清楚这将如何影响uri中的嵌入,因为AST仍然必须被重构、修改和重新编码。

泽尔科- Vrba
挪威奥斯陆

-------------------------------------------

作者的回应

LINQ的基础是从Haskell编程语言中借用的单子类型类。单子是一个管理单形容器结构的接口,包括列表、集合、包和排列。LINQ不预设或保证秩序。无论如何将一个图线性化,比如说,一个有序列表或无序包,LINQ都不会改变给定的顺序。

至于卷积、延续和异常,我只需要说明一个集合是单元的,就可以表明它也是LINQable的。对于卷积,在傅里叶表示下,LINQ作用于向量空间,也就是单子。关于延续和异常单子(称为Maybe单子),请参阅Haskell文档http://www.haskell.org/haskellwiki/Haskell。

Vrba和我将尊重地在辩论的顺序上有不同意见。我承认,机器重新排序是一个功能性的修复,但它也在调用站点引入了一个额外的步骤,出于风格的考虑,我宁愿避免。

布莱恩·贝克曼
雷蒙德,佤邦


显示所有2评论

登录全面访问
忘记密码? »创建ACM Web帐号
文章内容:
Baidu
map