编程语言描述过程,无法描述世界。


编程语言是人类最伟大的发明之一

编程语言是无歧义的。 x = 3 + 4 无论何时何地运行,结果都是7。 没有解释的余地。

编程语言是可验证的。 语法错误在编译前就能捕获。 类型错误在运行前就能捕获。 测试一旦运行,结果要么通过,要么失败。

编程语言是图灵完备的。 一切可计算的事物都能表达。 只要有足够的时间和内存,任何过程都能描述。

这个系列中指出的自然语言的局限——歧义、不可验证、缺乏结构——编程语言全部解决了。

那么,用编程语言来表达AI的上下文不就行了吗?

不行。


编程语言描述的是过程

下面是有效的Python代码。

def calculate_revenue(units_sold, unit_price):
    return units_sold * unit_price

这段代码清晰、可验证、可执行。 但它表达的是什么?

“将销售数量乘以单价,得到收入。”

这是一个过程。一种方法。是HOW。 它描述的是当输入到来时该做什么。

现在试着表达以下内容。

“三星电子2024年第三季度的收入为79万亿韩元。”

这不是过程。这是事实。是WHAT。 什么都不执行。它描述的是世界的状态。

用Python怎么表达这个?

samsung_revenue_2024_q3 = 79_000_000_000_000

把一个数字赋值给了一个变量。 能运行。但这不是描述,而是存储。 这段代码不知道:

  • “三星电子"是什么类型的实体。
  • “收入"是什么意思。是财务指标,还是物理量?
  • “2024年第三季度"是时间、版本,还是标签?
  • 79万亿韩元这个值的来源是什么。
  • 这个值有多确定。

变量名 samsung_revenue_2024_q3 让人类可以猜测其含义。 对机器来说,它只是一个任意字符串。 改成 xyzzy_42,执行结果完全一样。

在编程语言中,变量名不承载含义。 含义存在于代码之外,存在于程序员的脑海中。


更精细的方式也无济于事

创建一个类不就行了吗?

class FinancialReport:
    def __init__(self, company, metric, period, value, currency):
        self.company = company
        self.metric = metric
        self.period = period
        self.value = value
        self.currency = currency

report = FinancialReport("삼성전자", "매출", "2024-Q3", 79_000_000_000_000, "KRW")

好了一些。有了结构。 但问题依然存在。

company 是字符串"삼성전자”(三星电子)。 “Samsung Electronics”、“SEC"和"005930"都指同一家公司。 代码知道吗?不知道。 它只能比较字符串是否相等。

metric 是字符串"매출”(收入)。 “매출”、“매출액"和"revenue"是同一个东西吗? 代码不知道。字符串不同,所以它们就是不同的。

定义一个schema不就行了吗? 用Enum管理公司列表,管理指标列表。 可以。能运行。

那现在试着表达以下内容。

“李舜臣是伟大的。”

opinion = Opinion("이순신", "was", "위대했다")

这是什么? 一个字符串"이순신”(李舜臣)和一个字符串"위대했다”(伟大的)绑在了一起。 这不是在表达"李舜臣是伟大的”, 而是在存储"이순신"和"위대했다"。

代码不知道"위대했다"(伟大的)是什么意思。 “위대했다”(伟大的)和"훌륭했다"(优秀的)是否相近, “비겁했다”(懦弱的)是否相反—— 代码无从知晓。

像财务数据这样的结构化事实还勉强可以应对。 评价、语境、关系、抽象描述——这些都超出了编程语言的表达范围。


代码不知道自己在做什么

def process(data):
    result = []
    for item in data:
        if item["value"] > threshold:
            result.append(transform(item))
    return result

这段代码执行得完美无缺。 但这段代码是做什么的?

是在过滤收入数据? 是在筛选病历? 是在清洗传感器数据?

代码本身不知道。 datavaluethresholdtransform——全是抽象名称。 这段代码属于财务系统还是医疗系统, 取决于代码之外的上下文。

可以写注释。 但注释是自然语言。机器无法理解。 注释和代码不一致,编译器也不会发现。 注释是给人看的,不是给机器看的。

当AI接收代码作为上下文时,这个问题直接显现。 因为代码没有自我身份, AI每次都必须通过推理来重建它的身份。 因为是推理,所以有成本,而且可能出错。


根本原因

编程语言无法描述世界,这不是设计缺陷。 是因为目的不同。

编程语言的目的是向机器下达过程指令。 “当这个输入到来时,执行这个操作。” 编程语言的语义学是执行的语义学。 每一条语句都被解释为"机器做什么"。

x = 3 是指令"在名为x的内存位置存储3"。 if x > 0 是指令"如果x大于0,则执行下一个代码块"。 return x 是指令"将x的值返回给调用者"。

全是动词。全是行为。全是过程。

“三星电子是韩国的企业"不是动词。 不是行为。不是过程。 它描述的是世界处于什么状态。

编程语言中没有为此留出位置。 可以存入变量,但那是存储,不是描述。 存储的值的含义不在代码的管辖范围内。


JSON、YAML、XML呢?

不是编程语言,那数据格式呢?

{
  "company": "삼성전자",
  "metric": "매출",
  "period": "2024-Q3",
  "value": 79000000000000,
  "currency": "KRW"
}

有结构。字段是显式的。 但没有含义。

“company"是否表示企业,JSON不知道。 “삼성전자"和别处的"Samsung Electronics"是否是同一个东西,JSON不知道。 这个JSON对象和那个JSON对象是否描述同一个实体,JSON不知道。

JSON提供结构,但不提供含义。 它是键值对,不是实体-关系-属性。

定义schema会好一些。 JSON Schema、Protocol Buffers、GraphQL。 字段类型被定义了,必填性被定义了,引用被定义了。

但这些全是为特定系统设计的结构。 不是通用的知识表示。 财务数据schema无法表达对历史人物的评价。 医疗数据schema无法表达企业间的竞争关系。

每个领域一套独立的schema。 每套schema一套独立的工具。 schema之间没有互操作性。

这一局限在为什么MD/JSON/XML不够用中有更详细的讨论。


LISP呢?

读到这里的人中,有些可能已经想到了一个反例。

LISP。

(is 삼성전자 (company korea))
(revenue 삼성전자 2024-Q3 79000000000000)
(great 이순신)

S-expression是树结构, 代码即数据,数据即代码。 同像性(homoiconicity)。

事实上,早期AI全部基于LISP。 SHRDLU、CYC、专家系统。 用LISP表示知识,运行推理引擎。 似乎是对"编程语言无法描述世界"的历史性反证。

但这个反例因三个原因而不成立。

LISP知道的与程序员规定的

(is 삼성전자 company) 中,is 表示"是……“这种关系—— LISP并不知道这一点。 是程序员这么规定的。

is 换成 zzz,LISP毫不在意。 (zzz 삼성전자 company) 对LISP来说是完全有效的表达。

LISP提供的是结构——一种叫S-expression的树。 但结构中承载的含义不是语言赋予的,而是程序员赋予的。 这和JSON不知道其键的含义本质上是一样的。

提供结构与内嵌含义是两回事。

CYC的30年

最雄心勃勃地尝试这一点的项目是CYC。

始于1984年。 试图用LISP表示通用知识。 数百万条规则由人工输入。

30年证明的不是可行性,而是局限性。

每个领域都必须手工设计本体。 跨领域互操作行不通。 无法跟上自然语言的灵活性。 规模越大,维护一致性就越接近不可能。

用LISP"能做"知识表示,这没错。 “做得好”——这一点被30年的成果所否定。

如果不eval,就没有理由使用LISP

最根本的问题。

LISP的力量在于 eval。 因为代码即数据,所以数据可以被执行。 元编程、宏、运行时代码生成。 这才是让LISP成为LISP的东西。

但对 (is 삼성전자 company) 执行 eval 会发生什么?

它变成一个函数调用——将 삼성전자company 作为参数传递给名为 is 的函数。 不是描述,而是执行。

要用于知识表示,就不能eval。 如果不eval,就没有使用LISP的语义。 只是借用了S-expression的语法而已。

这不是"用LISP描述世界”, 而是"用LISP的括号记法存储数据”。

LISP作为编程语言的语义——执行的语义—— 仍然是为描述过程而设计的。 借用语法并不会改变语义。


描述世界的语言需要什么条件

编程语言描述过程。 数据格式提供结构但没有含义。 即使LISP也只是借用语法,没有描述的语义。

那么,描述世界的语言需要什么?

实体的身份。 “三星电子"必须拥有唯一标识符。机器必须知道它和"Samsung Electronics"是同一个东西。不是字符串比较,而是身份的同一性。

关系的表达。 在"三星电子是韩国的企业"中,必须能够表达"韩国的企业"这一关系。不是变量赋值,而是关系的描述。

描述的自我描述。 这个描述是关于什么的、谁说的、以什么时间为准、有多确定——这些都必须包含在描述本身中。在代码之内,而非之外。

领域独立性。 财务数据、历史事实、主观评价、抽象关系——都必须能用同一种格式表达。不是每个领域一套独立的schema,而是一种通用结构。

编程语言不具备这四个特性。 因为编程语言不是为此而造的。 它们是为描述过程而造的。

自然语言能做到这四点。但充满歧义。 我们需要的,是将自然语言的表达范围与编程语言的精确性结合起来。


总结

编程语言无歧义、可验证、图灵完备。 但无法描述世界。

编程语言描述的是过程。 “当这个输入到来时,做这个。” 全是动词,全是行为。 “三星电子是韩国的企业"不是行为。 编程语言中没有为此留出位置。

代码不知道自己的身份。 它属于哪个领域、执行什么目的—— 代码中没有记录。

JSON、YAML等数据格式提供结构,但没有含义。 LISP可以借用语法,但没有描述的语义。 CYC用30年尝试基于LISP的知识表示,证明的是局限性。

描述世界需要实体的身份、关系的表达、描述的自我描述、领域独立性。 编程语言不是为此而造的。 自然语言能做到,但充满歧义。 我们需要的,在两者之间的某处。