Verse Wiki — 写给 UE6 时代的 Verse 互动手册
拓展 · EXTRA

Verse 演算:给函数式逻辑编程一个数学核心

第 1 课说 Verse 是「函数式逻辑语言」——这个词不是营销话术,背后有一篇正经的学术论文。这一页带你看看 Verse 的数学心脏:变量靠合一绑定、表达式可以产出多个值、失败取代布尔值,这些怪设计从何而来。

一、一门游戏语言,一篇 ICFP 论文

2023 年,Simon Peyton Jones、Tim Sweeney、Lennart Augustsson 等八位作者在函数式编程领域的顶级会议 ICFP 上发表了论文《The Verse Calculus: a core calculus for deterministic functional logic programming》。工业界为自家脚本语言写形式化论文本就少见,更少见的是这份作者名单——语言的老板(Sweeney)和函数式编程的宗师(Peyton Jones)并排署名。

这篇论文做的事,可以用一个类比讲清楚:lambda 演算之于函数式语言,就是 Verse 演算之于 Verse。lambda 演算是一套极简的数学规则,所有函数式语言的行为最终都能还原到它身上;Verse 演算则是 Verse 的这套「最小内核」——把语法糖全部剥掉之后,剩下的那台不会骗人的裸机。Verse 演算就是 Verse 剥到只剩骨架后的「最小内核」。数学家惯用的招数,是给一门语言配一套极简、不会骗人的规则,让语言里发生的一切最终都能还原到这套规则上;把那些只是让代码写起来顺手的便捷写法全部剥掉之后,剩下的就是这台裸机。

二、内核三件套:合一、选择、确定性

Verse 演算的核心思想有三条,每一条都对应你在正课里会遇到的「怪设计」:

论文为这套内核给出了小步重写语义(small-step rewrite semantics)——程序如何一小步一小步地被改写、化简,并证明了对行为良好的项,这个重写系统是合流的(confluent):不管你按什么顺序化简,最终都汇到同一个结果。这意味着你可以像推导数学公式一样,对 Verse 程序做等价变换——编译器优化、程序验证,都因此有了坚实的地基。

三、把理论映射回你写的代码

听起来很玄?其实你在正课第 1 课就已经踩在这套理论上了。看两个最小示例:

calculus_taste.verse
# = 不是赋值,是一次「可失败的相等」
# 可失败表达式只能住在 if 这样的失败上下文里
if (2 + 2 = 4):
    Print("数学没有崩坏")

# for 遍历的是一段表达式的整个「成功空间」
for (I := 1..3):
    Print("回合 {I}")

读图翻译(蓝图视角):上半段就是一个 Branch 节点——只不过它问的不是 True/False,而是「2 + 2 = 4 这条线走不走得通」,走通了才接着打印。下半段是一个 ForEach Loop:1..3 相当于一个装着 1、2、3 的数组,循环体里那句 Print 会被逐个跑三遍,于是日志里出现三行「回合」。

第一段:2 + 2 = 4 是一次相等测试,成立则 if 的分支执行,不成立就安静地失败——没有 true/false 在代码里流动,「失败」本身取代了布尔值。第二段:1..3 在演算的视角下是一个产出 1、2、3 三个值的选择序列,for 把这个选择空间走了一遍,于是日志里出现三行「回合」。教程里的「掷骰子」「失败上下文」这些比喻,论文里都有一个精确的数学对应物。

如果想听作者亲口讲这套设计,Peyton Jones 与 Sweeney 在 Lambda Days 2023 的联合演讲《Beyond functional programming: a taste of Verse》是最好的入门材料——一小时里把「if 即失败上下文」「for 即遍历选择空间」讲得明明白白。

四、小测一下

论文证明了 Verse 演算的重写系统(对行为良好的项)具有哪条性质?

在 Verse 演算的视角下,for 表达式(也就是蓝图里的 ForEach Loop)在做什么?

来源与延伸

本文整理自 Simon Peyton Jones 个人主页的论文专页与 Lambda Days 2023 公开演讲: