2017 年 6 月

第 32 卷,第 6 期

C# - 使用 Hyperlambda 让 C# 更加动态化

作者 Thomas Hansen

以往,一直只有以下两种编程语言:静态编译型语言和动态解释型语言。如果实际上还有第三种编程语言,那会是什么情况?这是一种不属于上述两种类别的语言,如果将它与其他所有语言进行比较,就会发现其他语言的僵化程度令人绝望。

Hyperlambda 就是这第三种语言,即完全是在有效事件基础之上生成的图灵完备执行环境(请参阅我的上一篇文章,网址为 msdn.com/magazine/mt795187)。Hyperlambda 既不是解释型语言,也不是编译型语言。它甚至没有语法。Hyperlambda 是非编程语言,甚至可能是一种反编程语言。从技术角度来讲,它根本不算是编程语言。Hyperlambda 的核心就是可允许声明树结构的关系文件格式。可以说,它与 HTML 和 XML 的共同点要多于与 C# 和 JavaScript 的共同点。但其实树结构只是碰巧能够描述所谓的执行树。

执行树是 CPU 真正执行的内容。所有编程指令基本上都可以理解为 CPU 在计算代码时穿过的树分支。例如,if/else 代码块创建了两个互斥的可能分支,CPU 只从中选择一个,具体视 if 语句条件结果而定。实质上,计算机执行代码就是在穿过执行树,同时在进程中控制其内存。也就是说,如果可以描述执行树并控制内存,也就可以描述以前使用 C# 和 JavaScript 描述的任何内容。

一切皆是树

就像在 Lisp 中一切皆是“列表”一样,在 Hyperlambda 中一切皆是“树”。下面的示例展示了在经过分析后创建执行树的某 Hyperlambda 伪代码:

if:condition
  do-x:argument1
else
  do-y:argument1

分析此代码会生成 lambda 对象。通过在 Phosphorus Five (P5) 中调用“eval”有效事件,可计算此 lambda 对象或执行树。基本上可以将有效事件视为允许调用 C# 等方法且使用图对象或树对象作为其唯一自变量的“函数对象”。P5 实现的是有效事件设计模式。

创建此类 lambda 对象后,P5 中的“eval”有效事件便会从上到下依序执行根节点的子节点,将它们作为有效事件进行调用。上一示例中创建的 lambda 对象包含四个节点,其中两个为根节点。因此,在执行“else”之前,“eval”有效事件会先执行“if”。

所以,lambda 对象成为了 Hyperlambda 创建的“DOM 结构”。如果愿意,我本就可以使用 XML 替代 Hyperlambda 来描述此树结构。Hyperlambda 只是碰巧是描述此类树结构的最优方法。通常,将 Hyperlambda 视为 HTML 或 XML,并将 lambda 对象视为生成的 DOM 结构非常有助于理解。

Hyperlambda 的语法极其简单。所有节点都可以有名称、值和子节点。下面介绍了它的所有主要控制结构:

  • 冒号可分隔节点的名称和值。
  • 两个空格可打开节点的子集合。

节点值可以根据需要包含类型声明,即塞在名称及其值之间。尽管类型声明是可选的,且很少使用。在 Hyperlambda 中,字符串是默认类型声明。

Hyperlambda 有何独到之处

C++ 或 C# 等静态语言预先将代码编译成计算机可执行的某代码,然后再开始执行结果。JavaScript 等动态语言通常实时解释代码,同时执行代码。相比之下,Hyperlambda 的行为与这两种语言都不一样。而是声明由 P5 分析的执行树,从而生成 lambda 对象,随后可以在 P5 中使用“eval”有效事件计算此类对象。创建的 lambda 对象甚至不会考虑语法,可以使用任何能够声明关系树结构的文件格式来声明 lambda 对象。也就是说,Hyperlambda 最终不是编程语言,而仅是 HTML 或 XML。不过,由于它仍是图灵完备执行环境,因此集其他所有编程语言允许执行的全部操作于一身。可以说,我本可以使用 HTML 声明 lambda 对象,届时 HTML 就会成为图灵完备编程语言。

让树生成分支

Hyperlambda 中的每个关键字就是一个有效事件。调用这些有效事件通常会根据某条件以递归方式调用“eval”有效事件。这样一来,Hyperlambda 便极具扩展性。新建关键字实际上就是创建五行代码。下面是使用 C# 的一个例子:

[ActiveEvent (Name = "what-is-the-meaning-of-life?")]
private static void foo (ApplicationContext context, ActiveEventArgs e)
{
  e.Args.Value = 42;
}

事实上,if 和 else 关键字都是这样创建为有效事件的。

不必使用 C# 来创建有效事件或关键字。还可以在 Hyperlambda 中创建自己的关键字:

create-event:what-is-the-meaning-of-life?
  return:int:42

此时,善于观察的读者可能会意识到,尽管 Hyperlambda 绝对是非常高级的抽象,但从某种哲学层面来讲,它也是非常低级的抽象。可以说,远低于其他任何现有编程语言,因为修改的不是代码,而是真实的执行树。Hyperlambda 的这一特点具有巨大优势,就像 DOM 模型为 HTML 和 XML 带来了巨大优势一样。首先,可以非常灵活地自行修改代码。也就是说,创建代码和执行代码之间的常见差异变得更小了,即代码两边都沾点边。通过一些输入数据计算 lambda 对象,可以在执行树中轻松新建逻辑和其他分支。例如,如果由于某种原因而不想使用特定的 if/else 代码块,可以动态删除它,即使已开始计算包含它的 lambda 对象,也不例外。实际上,执行树可能会因向 lambda 对象提供的输入数据而完全不同。

就像可以使用 DOM 模型通过 JavaScript 修改 HTML 页面的部分一样,也可以使用“lambda 对象模型”在执行期间修改 lambda 对象的部分。执行树实际上就变成了可以在执行期间变化的动态实体。

来回转换 Hyperlambda 文件格式(具有可序列化和持续性)和 lambda 对象(即执行树)就像调用有效事件一样简单。

查找分支

Hyperlambda 还有其他不同寻常的特点,其中之一是它不含变量。我并不是说没有 F# 意义上的变量,也不是说一切都不可变。实际上,我是说 Hyperlambda 不含变量! 此时,大家当然会表示怀疑。毕竟,能够更改执行树和内存的状态是创建图灵完备执行环境的关键所在。不过,如果可以更改执行树的状态,实际上就无需历来所谓的变量。

在 Hyperlambda 中,一切皆可更改。在 Hyperlambda 中更改任何内容仅仅是指引用要更改的节点,然后提供更改操作的源。因此,首先需要引用树中的节点,这可通过 Lambda 表达式完成,如下所示:

_data:Thomas
if:x:/@_data?value
  =:Thomas
  say-hello:Yo boss
else
  say-hello:Yo stranger

此代码中显示了一个 Lambda 表达式,用于引用 _data 节点值,并作为自变量提供给 if 有效事件调用。此 if 调用会对比 Lambda 表达式的结果与静态值“Thomas”。如果“_data”的值等于“Thomas”,则会在 if 节点内计算 lambda 对象。否则,将会在 else 节点内计算 lambda 对象。

Lambda 表达式可以引用树中的任何节点,并根据原始树生成子树。Lambda 表达式与树结构之间的关系有点类似于 SQL 与表之间的关系。如果创建了 XPath 表达式,则可以轻松可视化 Lambda 表达式。如果愿意,可以将它们比作 LINQ 或“预编译枚举器”。后者实际上就是具体的实现方式。如果认为生成 LINQ 查询可动态添加值,那么单纯从逻辑上来讲,必须认为 Lambda 表达式可能拥有值。

修剪树

此时,只要能够更改树的部分,即可拥有图灵完备执行环境。随后,我将使用“set”有效事件实现此操作:

_input:Thomas
_output
if:x:/@_input?value
  =:Thomas
  set:x:/@_output?value
    src:Yo boss
else
  set:x:/@_output?value
    src:Yo stranger

“set”有效事件的工作方式基本上类似于 C# 中的赋值运算符。此时,我们有一个图灵完备“非编程语言”,可以执行过去常常使用传统编程语言执行的一切操作,无需创建编程语言,也无需考虑语法。相反,可以在计算 lambda 对象时直接修改执行树。

可用于修改树的有效事件还有很多。例如,“add”可将许多节点追加到 Lambda 表达式的结果中,从而额外生成一个或多个节点;“insert-­before”和“insert-after”的工作方式类似于“add”,不同之处在于它们是在许多节点的前后注入一个或多个节点。删除节点、值或名称非常简单,不用向 set 提供源,即可轻松删除节点及其值或名称,具体视用于表达式的类型声明而定。

使用此类构造,可以让 C# 成为超动态编程语言,为程序员提供直接更改执行树所需的工具。可以说,鉴于这些特点,Hyperlambda 成为目前为止最动态的编程环境。相较于 Hyperlambda,即使是 Lisp 和 JavaScript 等超动态语言,静态和僵化程度也会令人绝望。

此时,可能会意识到,在 Hyperlambda 中,数据和逻辑完全没有区别,逻辑就是数据,两者之间没有任何语义上的差别。也就是说,可以像更改数据一样轻松地更改逻辑,进而生成超动态执行模型,即执行树在执行期间可能会彻底更改。这真是太酷了,我们可以通过一些极富教益的用例来进行说明。

培育树

用例 1: 假设数据库中有 100 条记录。每条记录在一定程度上都包含 Hyperlambda。如果愿意,可以选择所有这些记录,将它们转换成 lambda 对象,然后追加到目标 lambda 对象中,随后计算合并的结果。甚至可以在这些记录的范围之外新建一个函数对象,将其保存到磁盘上的文件中,然后使用此文件,就像调用函数一样。

用例 2: 如果愿意,可以仅计算半个函数,即删除不想计算的函数部分。据我所知,其他任何编程语言均无此功能。这碰巧成为一项极其实用的功能。

用例 3: 如果需要,可以从五个不同的函数中提取一半 lambda 对象,即新建一个派生自原始函数“优质部分”的函数。

这是如何实现的? 一个 lambda 对象可转换其他任何 lambda 对象,就像 XSLT 文件可转换 XML 文件一样。如果不想在系统中执行任何 while 循环,不管是什么原因,都可以轻松创建 lambda 对象来转换所有 while 循环,并将它们转换成 for-each 循环,同时在后台将 lambda 对象作为自动进程运行。

为了方便理解,可以将这看作是 HTML 与其 DOM 模型的工作关系。在 Hyperlambda 中,修改 lambda 对象就像使用 JavaScript 在 DOM 中插入、修改或删除 HTML 元素一样简单。如果认为此功能向 HTML 添加值,那么单纯从逻辑上来讲,必须同意这可能会向“逻辑”添加值。 在软件开发方面,Hyperlambda 开创了全新的模式。

用例 4: 假设在 System42 中使用 CMS (bit.ly/2pwMOY9)。使用此 CMS,可以创建完全动态且可交互的 lambda 页面,而不是直接显示静态 HTML。有些人可能会认为这没什么了不起的,至少从理论上来讲 PHP 也可以执行相同的操作。不过,这些 lambda 页面还是可重用的组件。Hyperlambda 的动态性意味着,举例来讲,可以在执行 lambda 页面期间检查是否存在“parent­widget”自变量;如果存在,无需创建页面,即可将相同页面作为用户控件注入页面上的其他小组件。

实际上,甚至无需修改原始页面;而是可以在计算“create-widget”调用之前,先转换 lambda 对象。在 Hyperlambda 中,按照过去常常在传统编程语言中采用的方式进行绝对分类完全没有意义。

因此,在 Hyperlambda 中创建的每个 lambda 对象也可能是可重用的组件,可以将其注入系统的其他部分中。也就是说,可以递增方式创建越来越复杂的系统,即在先前创建的系统基础之上进行生成。在传统编程语言中,必须在生成系统或生成可重用组件之间进行选择。在 Hyperlambda 中,此类互斥选择毫无意义,一切往往变得所有都沾点边。Hyperlambda 的可重用性功能让大家之前通过编程了解的一切事物完全变得不值一提。

用例 5: 在 Hyperlambda 中,实际上可以递归方式执行磁盘上的文件夹,即向执行树传入自变量,同时消除传统编程常常有的一切约束。或者,可以完全像调用函数一样调用文件,让文件调用向其调用方返回多个值。还可以轻松地调用传送到服务器的 HTTP POST 请求的主体,从而使用 5 到 15 行代码有效地替换已创建的每个 Web 服务终结点。我几乎每天都要使用 Hyperlambda 执行所有这些操作。

用例 6: 在 Hyperlambda 中,甚至将网页单纯地看作是网页也毫无意义,因为只需替换应用池中的单个程序集,即可使用完全相同的代码轻松创建 Windows 窗体应用。在 Hyperlambda 中,将相同的基准代码重用于 Web 应用和 Windows 窗体应用其实简单至极,即使是代码的 GUI 部分,也不例外。

当我向其他软件开发者推荐 Hyperlambda 时,有些人往往会不予考虑,因为 Hyperlambda 似乎比传统编程语言更为冗长。例如,在 C# 中,只用一行代码即可轻松描述上面的代码示例,而在 Hyperlambda 中则需要九行代码。对于简单任务,得承认 Hyperlambda 有时会比较冗长。不过,Hyperlambda 具有一个重要特点,即能够转换 lambda 对象,这样从长远来看代码就不太冗长了,从而能够减轻这一问题产生的影响。因此,基本上前期会付出少许代价(即从短期来看代码较为冗长),但最终从长远来看代码就不太冗长了。这样,便能事半功倍。Hyperlambda 可以让软件工作效率呈指数式增长。请多理解理解最后一句话。

Hyperlambda 的 Hello World

下面展示了在 Hyperlambda 中创建的“Hello World”应用。如果将以下代码粘贴到 System42 CMS 中的 lambda 页面等位置,可以创建一个在获得单击后将其内部值更改为“I was clicked”的按钮。 下载 P5,并尝试从应用/CMS 应用入手。然后,创建 lambda 页面,并在其中粘贴以下代码:

create-widget:foo-widget
  element:button
  class:btn btn-default
  innerValue:Click me!
  onclick
    set-widget-property:foo-widget
      innerValue:I was clicked

可以说,7 行 Hyperlambda 代码可能替换了数百行 JavaScript、C# 或 HTML代码,具体视创建相似内容所使用的框架、库或语言而定。如果仔细阅读此代码,可能会猜到将生成的 HTML 标记。与其他编程语言相比,Hyperlambda 往往更接近于自然语言。从语言角度来讲,相较于 C# 或 JavaScript 等语言,它更类似于人类自然地向他人传递信息的方式。

还有一项额外好处就是,如果使用上述 create-widget 有效事件的备用实现代码创建自己的 Windows 窗体程序集,可以使用完全相同的代码创建 Windows 窗体应用。

Hyperlambda 是妙方吗?

一直以来,我都在提倡使用有效事件这一妙方,也因此受到指责。我承认,我的一些言论可能听起来神乎其神。Hyperlambda 最初确实会创建较为冗长的代码,并增加编程指令执行开销,这是由其内部实现语义决定的。我也偶尔会发现 bug,并进行重构,希望能够改进代码。Hyperlambda 显然并不完美。

不过,Hyperlambda 的最大问题实际上是大家不接受它。Hyperlambda 让过去 50 年来形成的软件开发最佳做法化为泡影。它自成一派,把钱花在该花的地方!

然而,归根结底,我无法明确回答 Hyperlambda 是否是妙方。大家需要自己寻找答案。请查看完全是在 Hyperlambda 中生成的 GMail 克隆工具 Sephia Five (github.com/polterguy/sephia-five),这可能会有所帮助。Sephia Five 具有 PGP 加密功能,并能完全防御病毒和恶意软件,适用于可以显示 HTML 的所有设备。在带宽使用方面,Sephia Five 的效率也比 GMail 高几个数量级。实际上,从零开始生成它的初版只花了五天。所有这些都有据可查,并能进行数学上的重现和证实!

若要详细了解 Hyperlambda,欢迎阅读 github.com/polterguy/phosphorusfive-dox 处的指南。若要下载 Hyperlambda 及 Phosphorus Five,请访问 github.com/polterguy/phosphorusfive


Thomas Hansen 自 8 岁起便一直在开发软件,他于 1982 年就开始使用 Oric-1 计算机编写代码。编写的代码偶尔确实是利大于弊。他对 Web、Ajax、敏捷方法和软件体系结构充满热情。

衷心感谢以下 Microsoft 技术专家对本文的审阅: James McCaffrey