下载本文的 PDF 版本 PDF

Hop 中的多层编程

迈向 21 世纪应用程序编程的第一步


Manuel Serrano 和 Gérard Berry,INRIA


Web 正在成为创建计算机应用程序的最丰富的平台。其力量来自三个要素:(1)现代 Web 浏览器支持高度复杂的 GUI,包括 3D、多媒体、精美排版等;(2)通过 Web API 调用现有服务使得从独立可用的组件开发复杂的应用程序成为可能;(3)开放数据可用性允许应用程序访问以前无法访问或根本不存在的广泛信息。这三个要素的结合已经催生了革命性的应用程序,例如 Google 地图、播客和社交网络。

下一步很可能是将物理环境融入 Web。最新的电子设备配备了各种传感器(GPS、摄像头、麦克风、金属探测器、语音命令灵敏度、温度计、运动检测等)和通信方式(IP 协议栈、电话、短信、蓝牙等),这使得应用程序能够与现实世界互动。Web 浏览器正在一个接一个地集成这些功能,使 Web 运行时环境日益丰富。未来充满吸引力,但仍然存在一个难题:当前的编程方法和语言并不完全适合实现丰富的 Web 应用程序。这并不奇怪,因为大多数方法和语言都是在 20 世纪发明的,那时 Web 还不是现在的样子。

传统的编程语言在处理 Web 应用程序的不对称客户端-服务器架构时遇到困难。确保分布式客户端-服务器执行的语义一致性具有挑战性,并且传统语言缺乏对物理分布的透明支持。因此,程序员需要掌握复杂的技巧来处理分布式应用程序,并且通常为客户端和服务器使用不同的语言。JavaScript 是主要的 Web 语言,但最初被认为是仅用于浏览器的客户端语言。服务器通常使用截然不同的语言进行编程,例如 Java、PHP 和 Ruby。最近的实验,例如 Node.js,提出了在服务器上使用 JavaScript,这使得开发更加一致;但是,仍然不能确保独立组件的和谐组合。

2006 年,三个不同的项目——即 GWT(Google Web Toolkit)、爱丁堡大学的 Links1 和 INRIA 的 Hop (http://www.inria.fr)5——提供了编程 Web 应用程序的替代方法。它们都提出,Web 应用程序应该作为服务器和客户端的单个代码进行编程,并使用单一的统一语言编写。这个原则被称为多层编程

Links 是一种实验性语言,其中服务器不保持状态,并且可以从两侧对称地调用函数,允许它们在服务器或客户端上声明。这些功能对于探索新的编程思想绝对有趣,但它们难以有效实现,使得该平台难以用于实际应用程序。

GWT 更加务实。它将传统的 Java 编程映射到 Web。GWT 程序看起来像传统的 Java/Swing 程序,编译为服务器端的 Java 字节码和客户端的 JavaScript。然而,Java 不能被认为是 GWT 的唯一语言。调用外部 API 依赖于 Java 扩展中包含 JavaScript。GUI 基于外部 HTML 文件中声明的静态组件和客户端执行生成的动态部分。因此,至少 Java、JavaScript 和 HTML 直接参与其中。

Hop 语言采取了另一条路径,依赖于不同的想法:将所有必需的 Web 相关功能集成到一种单一语言中,该语言具有单一的同构开发和执行平台,统一覆盖 Web 应用程序的所有方面:客户端、服务器端、通信以及对第三方资源的访问。Hop 体现并概括了 HTML 和 JavaScript 功能,在一个基于 Scheme3 的平台上,该平台还为用户提供了完全通用的算法语言。Web 服务和 API 可以像标准库函数一样轻松使用,无论是在服务器端还是客户端。

多层编程和 HTML 抽象

本文介绍了 Hop 的 Web 应用程序编程方法,从 Web 文件查看器的基本示例开始。在第一个版本中,文件和目录列在一个空白页面上。目录是可点击的,以实现动态浏览,并显示纯文件名。这个简单的问题说明了实际 Web 应用程序最核心的方面——即服务器和客户端的紧密协作和同步。在这里,服务器拥有文件,而浏览器可视化它们。当用户点击目录时,客户端从服务器请求新信息。

收到后,客户端相应地更新页面。如果每个请求都交付一个新的完整页面,则文件查看器很容易实现。通过添加文件查看器应该是一个嵌入在复杂 Web 页面中的小部件的要求,问题变得稍微困难一些,因为更新现在只涉及文档的增量子部分。这需要服务器和客户端之间的新型协作。

使用单个 Hop 程序实现这样的 Web 客户端-服务器文件查看器几乎与为单台计算机实现它一样简单,因为 HTML 是内置的,并且服务器和客户端之间的执行划分是自动的。图 1 显示了此问题的完整 Hop 解决方案。

Hop 标识符可以包含特殊字符,例如“<”和“?”。在 Hop 中,directory?, string=?<SPAN>是合法的标识符。在示例中,<BROWSER>是绑定到创建 HTML 的函数的变量名称div元素。

HTML 对象由具有相同名称的 Hop 函数创建(<DIV>...), (<SPAN> ...)等等;不需要 HTML 闭包,因为代码处理的是括号功能表达式而不是文本。Scheme 的全部功能用于构建DIVs 和SPANs。辅助函数<dir-entry><file-entry>用于构建最终 HTML 文档的片段。主函数中的map操作<directory>用于在 Hop 的帮助下,从这些 HTML 片段构建全局 HTML 页面sort函数对打印输出进行字母排序。图 2 显示了<div-entry>函数生成的值如何编译成 HTML 和 JavaScript。

图 1 第 7 行出现的 ~ 和 $ 构造是多层编程

运算符。让我们解释一下它们是如何工作的。以 ~ 为前缀的表达式是客户端表达式,由浏览器评估;未注释的表达式在服务器上评估。代码在服务器上编译。客户端代码被视为一个值,由服务器计算或详细说明,并在需要时自动发送到客户端。$ 构造用于在详细说明时将服务器值注入到客户端代码中。例如,~(alert $(hostname))在客户端屏幕上打印服务器名称。在图 1 的示例中,第 7 行在图 2 的 HTML 片段中引发自动生成的名为anonymous314的服务的注入。第 7 行的~~前缀表达式指定用户单击目录时客户端要执行的操作。该操作调用匿名服务,该服务由客户端调用并在服务器上运行。

服务器和客户端之间的运行时通信涉及扩展函数概念的服务。服务是将函数绑定到 URL。URL 用于远程调用该函数,使用特殊形式(with-hop (<service> <arguments>) <callback>)。参数被编组以进行网络传输,并启动远程过程调用;在远程调用完成后,在调用者端调用回调,并使用解组的远程调用结果。额外的选项控制如何处理错误和超时(本文未介绍这些选项)。

此处的示例包含在<dir-entry>辅助函数中定义的一个匿名服务。每次用户单击目录时都会调用此服务。它调用<directory>函数,该函数遍历服务器文件系统。由于服务与函数一样是静态作用域的,因此服务代码中的<directory>标识符引用<directory>服务器函数,该函数稍后在同一相互递归的 define 子句中定义。参数path<dir-entry>函数的定义绑定,并在客户端代码中的服务调用中使用。词法绑定对于服务器和客户端代码是相同的。

一旦<BROWSER>函数被定义,它可以像 Hop 中任何常规 HTML 元素中的新 HTML 标签一样自由使用。例如,图 3 中显示的代码在表中实例化了两个文件浏览器。

<BROWSER>进行修改,从而影响服务器和客户端都非常容易。图 4 显示了<file-entry>函数体的单行修改,该修改显示文件名和大小。

Hop 依赖于混合的动态/静态类型规则。当变量或函数用类型注释时,编译器使用此类型来静态检查类型正确性并改进生成的代码。为了检测编译时错误,Hop 不与 ML 或 Haskell 等静态类型语言竞争,而是用表达性换取静态保证:例如,它支持静态类型语言无法触及的编程结构,例如类似 CLOS 的面向对象编程风格。另请注意,HTML 节点的子节点可以是任何类型(列表、字符串、数字、另一个节点等),而无需类型注释,正如 W3C 关于 HTML 的 XML Schema 建议所指定的那样。在示例中,只有 path 变量被类型注释。

Hop 小部件可以声明自己的 CSS(层叠样式表)规则,以抽象其实现细节。图 5 中的声明创建了一个与<BROWSER>标签关联的新 CSS 类型。图 6 显示了如何使用浏览器 CSS 类型在目录和叶节点之前添加额外的图标。

在 Hop 中,服务器上的 HTML 对象是完整数据结构的成员,该数据结构实现了客户端 HTML 文档的抽象语法树;这与大多数 Web 环境形成对比,在这些环境中,它们被简化为字符的裸字符串。Hop 服务器计算详细说明了类似于浏览器使用的 HTML 的 DOM(文档对象模型)表示,动态编译为实际的 HTML,并在浏览器请求时发送它。

这种多阶段过程消除了传统 Web 框架的几个缺点。首先,Hop 运行时环境保证了生成 HTML 的几个属性,例如语法正确性——例如,它将 HTML 标签拼写错误报告为未绑定变量错误。其次,单个文档可以根据需要自动编译成不同的 HTML 版本。同一个文档可以有选择地编译成 HTML4 和 Flash 的混合版本以用于旧浏览器,编译成 HTML5 以用于更高级的浏览器,甚至编译成用于语义注释的 XHTML+RDFa。

大多数当前的客户端 Web 库通过提供一组 JavaScript 函数来抽象 HTML,这些函数接受常规 HTML 节点作为参数;通常,DIVSPAN<DIV>被用作新的小部件容器。这种方法有几个缺点。首先,HTML 扩展看起来不像常规标签。因此,GUI 的实现需要组装不同的形式主义。其次,扩展初始化很难安排。通常,应用程序必须求助于窗口onload

JavaScript 事件,以确保在 DOM 树完全构建之前不会调用 API 构造函数。第三,为了配置扩展的图形渲染,必须公开实现细节,以允许程序指定要配置的 HTML 元素。这会危及可维护性。

代码重用

此处介绍的文件查看器应用程序涉及 Web 应用程序的主要要素——分布式架构、HTML 抽象和 CSS 配置——但它仍然过于简单而不够实际。为了以相对较小的努力开发更丰富的 Web 应用程序,现在通常的做法是依赖于广泛公开可用的 JavaScript API 集。所有 Web 框架都以某种方式提供使用它们的方法,通常是通过后门,让程序员将外来 JavaScript 调用插入到本机支持的语言中。

Hop 遵循不同的方法,通过将这些 API 集成到其语言中。从 Hop 调用 JavaScript 函数或创建 JavaScript 对象就像操作标准 Hop 实体一样容易。此外,一旦编译,客户端 Hop 生成的 JavaScript 只能通过用于将 Hop 标识符映射到 JavaScript 标识符的名称修改与手写 JavaScript 区分开来。其他一切都相同:Hop 数据、变量和函数直接映射到它们的 JavaScript 对等物。<BROWSER>JavaScript API 在 Hop 中的直接集成使得通过组件组合轻松进行 Web 平台开发。为了说明 API 集成,我们展示如何编写一个 PDF 查看器,该查看器以实际书籍的翻页效果显示文件内容。图 7 显示了此应用程序的快照,图 8 显示了实际源代码。它基于 jQuery,一个流行的 JavaScript 库;一个名为 turn.js (http://www.turnjs.com) 的 jQuery 插件,它实现了翻页效果;以及 Mozilla 基金会实现的 JavaScript PDF 预览器 (https://github.com/andreasgal/pdf.js)。假设 PDF 预览器以与

第一个呈现的方式相同的方式打包。在代码中,方法 turn 初始化 turn.js 插件。它准备一个 HTML 元素以用作书籍容器。此 JavaScript 方法直接用作常规 Hop 函数。turn.js API 在翻页之前和之后自动调用 JavaScript 监听器。此功能可以在服务器上按需生成页面内容。图 9 展示了一个完整的示例,其中客户端在翻页之前立即从服务器请求页面内容。此示例使用 turn.js 方法bind

将匿名 Hop 函数绑定到用户事件,在本例中,将用户函数与翻页事件关联。这表明 Hop 函数可以像常规 JavaScript 函数一样直接被 JavaScript 调用。

除了 Web 之外,没有任何平台曾经如此简单地通过组合不同方提供的 API 和代码来编写复杂的 GUI。

开放数据

政府和公共组织最近意识到,他们生成的数据是一项宝贵的资产。Data Publica 等新公司成立的唯一目的是收集、组织和重新分发开放数据。

传统上,开放数据是 Web 的外源。例如,法国政府机构 INSEE(国家统计和经济研究所;http://www.insee.fr)成立于 20 世纪中期,负责对人口和经济进行各种统计分析。INSEE 已成为重要的开放数据提供商。

令人惊讶的是,数据也可能是内生的。谷歌的流感趋势分析是这种类型的一个显著例子。谷歌研究人员观察到,关于流感的搜索请求的数量和位置与疾病的传播密切相关。2 谷歌按国家/地区提供此数据,使任何人都可以轻松实现使用这些统计数据的应用程序。

在本节中,我们将演示如何创建一个视频,显示流感随时间的传播。这主要是一个工具组合问题,因为所有困难的工作都可以委托给外部方。因此,此处显示的工作仅包括收集、组合和重用我们可用的所有工具和数据。实际的 Hop 实现(如图 10 所示)不超过 15 行代码。

应用程序代码依赖于一个绑定,该绑定使 Google Chart API 可以直接从 Hop 访问。此 Web API 创建各种图表和地理地图,根据在适当 URL 上指定的参数生成图像。此 API 与流感统计数据的提取相结合。首先,图像 URL 是从使用 Hop 电子表格元素库解析的统计值中生成的。然后,由 Google Chart 动态生成的图像一个接一个地显示,每 300 毫秒一次。

此示例表明,当可以轻松访问收集、比较和分析大量开放数据集的能力时,可能会涌现出新的和深刻的知识。Hop 专门为此目标而设计,组合和重用是两个核心功能。

比 Web 更大

通过绑定来自物理环境的传感器和执行器,例如智能手机传感器、多媒体设备、家庭或汽车自动化设备以及围绕我们的无数其他电子设备提供的传感器和执行器,可以使人类 Web 变得更大。然而,只有其中最流行的那些将被下一个 Web 浏览器原生支持。例如,即将推出的 Firefox 和 Chrome 导航器已宣布支持视频和音频捕获;通过浏览器或编程应用程序访问其他远程设备仍然需要第三方支持。Hop 的理念是将远程接口设施迁移到服务器,在关联数据上为用户提供完整的 Hop 编程能力,并在使用基于浏览器的界面时以标准方式向客户端交付结果。

图 11 显示了如何实现一个 Hop 应用程序,该应用程序将移动电话提供的功能集成到 Web 应用程序中。传入短信的内容显示在浏览器窗口中,让用户选择是否应朗读每条短信。

此应用程序使用三个分离层:Web 浏览器、Web 服务器和电话服务器。电话服务器负责接收短信并按需朗读它们。Web 服务器充当协调器的角色;当电话通知它收到新短信时,它会提醒感兴趣的客户端,并根据用户配置朗读消息。该应用程序利用了 Hop 的另一个有用功能:允许服务器成为客户端或其他服务器的新的程序生成 Web 事件的来源。在电话示例中,服务器开始与电话建立桥梁(使用android变量),然后等待 SMS 通知并访问电话文本转语音设施(使用tts

变量)。

服务器代码注册一个函数,该函数在每次收到新短信时被调用,以将软件 Web 事件转发给感兴趣的 Web 客户端。

实现

Hop 是一个开源项目,其源代码可以从网站 http://hop.inria.fr 下载。本文中显示的所有实际源代码也可以从该网站下载。开发工具包包含编译器、交互式文档、用于创建和安装应用程序的各种工具以及运行时环境,运行时环境是一个专用的 Web 服务器,可在所有 Un*x 平台(Linux、Mac OS X 和 Android)上运行。它可在主流现代架构(如 x86/32、x86/64、PowerPC 和 ARM 系列)上运行。

Hop 运行时环境仅需要 3-4 MB 的 RAM。这种小内存占用使其适用于智能手机甚至更小的机器,例如新的 ARM 计算机(Phidget SBC2、Raspberry Pi 等),这些计算机仅为应用程序提供几兆字节的内存。至于速度,Hop Web 服务器交付动态 Web 页面的速度明显快于传统的服务器(如 Apache 或 Lighthttpd)。性能提升是 Hop-in-Hop 实现的结果,这使得服务器能够响应涉及动态计算的请求,而无需昂贵的执行上下文切换。4

一个新的游乐场

Hop 是 Web 的多层编程语言和运行时环境的结合。其主要目标是统一 Web 应用程序所需的语言功能,以完全透明的方式自动化代码的物理分布,并帮助程序员重用和组合外部资源。所有这些都是程序简洁和简明扼要的关键。

本文介绍了几个用 Hop 编写的完整 Web 应用程序示例。第一个示例说明了如何通过使用算法编程语言方法来提高 HTML 抽象级别。第二个示例显示了如何重用第三方代码。第三个示例表明,将 Web 技术与开放数据相结合为新的应用程序创意开辟了道路。最后一个示例介绍了一个简单的 Web 上的分布式应用程序。选择所有这些示例都是因为它们的简单性;在处理家庭自动化或多媒体等领域中更大的应用程序时,相同的技术同样有效。

此处介绍的所有应用程序都没有超过 30 行 Hop 源代码。这不是偶然的;这是一种感知转变的结果,在这种转变中,Web 不再被视为仅仅是共享文档的平台,而是被视为革命性的运行时环境。但是请注意,Hop 是一种实际的编程语言,这意味着有很多额外的花哨功能。本文忽略了其中大部分,专注于多层编程以及与官方 Web 技术的连接。

最后,请注意,Hop 核心语言是一种严格的函数式编程语言,具有运行时类型检查。这种设计选择主要来自第一作者的个人偏好。可以说,它非常适合当前 Web 的编程趋势,但其他风格应该是可能的,从而产生其他编程语言。我们希望看到这种新语言的出现,因为方法的多样性将促进创造力,并可能开启语言和工具设计的新时代。

参考文献

1. Cooper, E., Lindley, S., Wadler, P., Yallop, J. 2006. Links:无层 Web 编程。在第 5 届组件和对象形式化方法国际研讨会上发表。

2. Ginsberg, J., Mohebbi, M., Patel, R., Brammer, L., Smolinski, M., Brilliant, L. 2009. 使用搜索引擎查询数据检测流感爆发。自然 457 (2 月 19 日): 1012-1014。

3. Kelsey, R., Clinger, W., Rees, J. 1998. 关于算法语言 Scheme 的修订 (5) 报告。高阶和符号计算 11(1); http://www-sop.inria.fr/indes/fp/Bigloo/doc/r5rs.html

4. Serrano, M. 2009. Hop,用于分布式 Web 的快速服务器。在第 11 届协调模型和语言国际会议论文集,里斯本,葡萄牙。

喜欢它,讨厌它?请告诉我们

[email protected]

MANUEL SERRANO 是 INRIA 的高级科学家,领导 Sophia-Antipolis 的 INDES(分布式和安全信息学)团队。在皮埃尔和玛丽·居里大学 (UPMC) 完成关于函数式语言编译的博士学位后,他搬到尼斯并创建了 Scheme 的 Bigloo 开发环境。他于 2001 年加入 INRIA,自 2005 年以来一直专注于分布式 Web 的开发环境。

GÉRARD BERRY,INRIA 的研究主任,致力于编程语言、它们的数学语义和程序验证技术。他的重点是 lambda 演算及其模型;反应式和实时编程和验证;以及高级数字电路规范、综合和验证。在加入 INRIA 之前,他是 Esterel Technologies 的首席科学家,并且是 Esterel 语言的主要作者,该语言已在学术界和工业界用于从复杂电路综合到飞机控制的应用。

© 2012 1542-7730/12/0600 $10.00

acmqueue

最初发表于 Queue 第 10 卷,第 7 期
数字图书馆 中评论本文





更多相关文章

Shylaja Nukala, Vivek Rau - 为什么 SRE 文档很重要
SRE(站点可靠性工程)是一种工作职能、一种思维模式以及一组工程方法,用于使 Web 产品和服务可靠运行。SRE 在软件开发和系统工程的交叉点运作,以解决运营问题并设计解决方案,以可扩展、可靠和高效地设计、构建和运行大型分布式系统。成熟的 SRE 团队可能拥有与许多 SRE 职能相关的明确定义的文档主体。


Taylor Savage - 组件化 Web
在当今的软件工程中,没有什么任务能像 Web 开发那样艰巨。Web 应用程序的典型规范可能如下:该应用程序必须跨各种浏览器工作。它必须以 60 fps 的速度运行动画。它必须立即响应触摸。它必须符合一组特定的设计原则和规范。它必须在几乎所有可以想象到的屏幕尺寸上工作,从电视和 30 英寸显示器到手机和手表表面。它必须经过精心设计,并且在长期内可维护。


Arie van Deursen - 超越页面对象:使用状态对象测试 Web 应用程序
Web 应用程序的端到端测试通常涉及通过 Selenium WebDriver 等框架与 Web 页面进行棘手的交互。隐藏此类 Web 页面复杂性的推荐方法是使用页面对象,但首先要回答一些问题:在测试 Web 应用程序时,应该创建哪些页面对象?页面对象中应该包含哪些操作?给定页面对象,应该指定哪些测试场景?


Rich Harris - 消除入门障碍
一场战争正在 Web 开发领域展开。一方是工具制造者和工具使用者的先锋,他们靠破坏糟糕的旧观念(在这个环境中,“旧”意味着任何在一个月前在 Hacker News 上首次亮相的东西)以及关于转译器等的喧闹辩论而蓬勃发展。





© 保留所有权利。

© . All rights reserved.