Fun语言考察报告 - glm5.2
..
作者: 一了 <
[email protected]
> 日期: 2026-06-30 AI 做的Fun语言考察报告, 联名作者: 一了, 智谱。
# FUN 语言考察 > 一个被时代遗忘的个人全栈 Lisp Machine > 从机器码到 UI 的 21 年三代演化史 > > 整理自与 FUN 设计者的多轮深度对话 · 2026 年 6 月 --- ## 目录 - [引子:被时代遗忘的语言](#引子被时代遗忘的语言) - [第一章 来龙去脉:三代家族的 21 年血脉](#第一章-来龙去脉三代家族的-21-年血脉) - [第二章 设计宪法:简单、直观、一致 + 人的效率](#第二章-设计宪法简单直观一致--人的效率) - [第三章 语言内核:40 关键字 + 三件套](#第三章-语言内核40-关键字--三件套) - [第四章 三种美学:暴力、温柔、二进制](#第四章-三种美学暴力温柔二进制) - [第五章 DSL 工具链:BNF → 数据 → 引擎 → 模板](#第五章-dsl-工具链bnf--数据--引擎--模板) - [第六章 全栈生态:从机器码到 UI](#第六章-全栈生态从机器码到-ui) - [第七章 特性降级:从语言级到库级到数据级](#第七章-特性降级从语言级到库级到数据级) - [第八章 文档哲学:案例驱动 + 自执行](#第八章-文档哲学案例驱动--自执行) - [第九章 数据格式 FD:兼容 JSON 的类 MD 格式](#第九章-数据格式-fd兼容-json-的类-md-格式) - [第十章 局限与代价:被遗忘的代价](#第十章-局限与代价被遗忘的代价) - [第十一章 终章:FUN 给软件工程的启示](#第十一章-终章fun-给软件工程的启示) - [附录一:FUN 的十个"全球独一份"设计](#附录一fun-的十个全球独一份设计) - [附录二:FUN 家族 21 年考古档案](#附录二fun-家族-21-年考古档案) --- ## 引子:被时代遗忘的语言 在主流编程语言版图里,Python、JavaScript、Java、Go、Rust 占据了几乎所有聚光灯。但在 Windows 平台的某个安静角落,存在一门由一位程序员独立维护了十六年的语言——**FUN**。它不是 Python 的同类,不是 JavaScript 的同类,甚至不是 Lua 的同类。它是一类被时代遗忘的"个人全栈语言":从机器码到 UI、从汇编器到 ORM、从模板引擎到 CSS 编译器,全部由一个人、一种哲学、一套原语贯穿始终。 本文不打算评判"FUN 是否应该流行"——这种评判对一门单作者语言没有意义。本文要做的是**理解**:理解 FUN 的设计宪法、内核三件套、三种美学、DSL 工具链、全栈生态、演化史与代价。理解一名工程师如何在二十多年里,用"简单、直观、一致"的三原则过滤每一个特性,用"机制一样,尽量减少人工"的准则驱动每一次设计,最终打磨出一门从 VirtualAlloc 到 HTML 组件、从机器码到模板引擎都自洽的完整语言。 这是一份考察报告,不是推荐文。FUN 有它的真实局限——Windows-only、单作者、生态小、无 async/await 语法糖、异常无类型化——这些都会在后文如实呈现。但在局限之外,FUN 也展示了一种被主流软件工程遗忘的可能性:**一个人,二十多年,能做出什么**。它给出的答案,远比"另一门脚本语言"这个标签所暗示的更丰富。 全文共十一章,按"来龙去脉 → 设计宪法 → 内核 → 美学 → 工具链 → 生态 → 降级哲学 → 文档 → 数据格式 → 局限 → 启示"的顺序展开。每章力求独立可读,但整体构成一个完整的认知闭环:读完这十一章,你应该能回答"FUN 是什么、为什么这样、怎么走到今天、值得被记住什么"。 --- ## 第一章 来龙去脉:三代家族的 21 年血脉 ### 1.1 三个诞生日,一条血脉 FUN 不是 2010 年凭空诞生的语言。它是从 2005 年开始、跨越三代、由同一位作者持续演化的家族产物: ``` 2005-09-13 templatescript 想法产生 2005-09-14 Bnf Created + Bnf2Regex&Html Created 2005-09-15 TemplateScript Created(变量/函数/控制流/闭包雏形/文件) 2006-01-15 nuva/tscript 想法产生 2006-01-16 tsLex.l + tsYacc.y + make.bat 2006-01-17 Lex Test Success 2006-01-18 写《TScript 总结、展望与随想》 2010-01-15 FUN 想法产生,开始设计 AdaScript 2010-01-20 开始写代码 2010-02-09 跑通 hello-world.fun 2010-03-22 性能超过 nuva 与 Ruby 2010-04-07 所有特性完成,开始写手册 2010-04-14 手册完成 2010-04-26 开始写库 2010-12-31 网站上线 2011-01-15 进入维护期 ... 2026-06-14 加入 pipe(|)运算符 ``` 最惊人的是 **nuva 和 FUN 都是 1 月 15 日产生想法**——这是作者个人的"语言设计节律",每隔几年在同一个日期启动新一代。委员会语言没有这种诞辰,单作者语言有自己的仪式感。 ### 1.2 templatescript:家族基因的原始 DNA 2005-09-14 的 BNF 是 FUN 家族最古老的文献。这份 BNF 不只是文档,是**可执行规范**——通过 `Bnf2Regex&Html` 工具直接生成正则,正则直接当解析器用。这是"BNF 即解析器"的极致,比主流语言"BNF → lex/yacc → C 代码 → 编译"的流程少两步。 这份 BNF 里已经埋下了 FUN 后来所有核心基因的种子: | 基因 | 2005-09-14 BNF | 演化方向 | |------|---------------|---------| | 标识符规则 | `[_A-Za-z]+\w*` | 后续扩展 `$` 和 `@` | | Ada-like 块结构 | `If.../If`、`For.../For` | 改为 `end X` 收尾 | | 控制流关键字 | `If/Case/For/ForEach/Function` | 小写化 + 简化 | | 属性访问 | `obj.prop`(点号) | 21 年不变 | | 命名参数 | `name = value` | 改为 `name: value` | | BNF 驱动 | BNF → 正则 → 解析 | 升级为 lex/yacc,但库层仍用正则 | | 一源多输出 | Bnf2Regex&Html 同时输出正则和高亮 | 演化为 z-data 的 pattern + monaco | 最关键的是 2005-09-15 的 `Parent Variable and Function Auto Cached`——这是闭包的雏形。templatescript 2005 年就支持子函数访问和修改外层变量,比 Java 的 lambda(2014)早 9 年。 ### 1.3 nuva:被迫演化的转折点 nuva 是 templatescript 的成熟版——用 lex/yacc 替换了"正则当解析器"的方案,加入了完整的 IDE、模板语法(`<. .>`)、system.\* 命名空间。但 nuva 有一处致命借鉴——它学了 BASIC 的"无参数调用不需要括号"。 这个看似友好的语法,实质上破坏了"函数一等公民"的语义:当一个标识符既可以表示"调用函数"又可以表示"引用函数本身"时,函数就无法作为值传递、无法作为参数、无法作为返回值。Python 2 的 print 语句到 Python 3 的 print 函数迁移,是同一类问题在主流语言中的版本,社区用了十年才完成。 > nuva 当时过于宽容,尤其是学了 BASIC 的糟粕(无参数调用不需要括号),从而无法方便地搞函数式。FUN 语言是被迫演化的新语言,二者从此独自进化。 > —— FUN 设计者 FUN 的作者选择了更激进的路径:**直接重开一门语言**。nuva 保留为模板脚本继续独立演化,FUN 从第一天起就强制所有函数调用必须带括号,从根本上避开了这个陷阱。这种"破旧立新"的勇气,是单作者语言的特权——委员会语言背负着向后兼容的包袱,做不到这种激进修正。 ### 1.4 重写 + 传承的演化机制 每一代 FUN 家族语言都是**全新写**的——不 fork 旧代码,只传承知识与经验。代码全新写让每代都干净,不带前代的包袱;知识与经验传承让每代都成熟,站在前代的肩膀上。 **传承的 7 类知识**:BNF 驱动的形式化思路、Ada-like 块结构、闭包语义、0-based 索引、默认参数 null、IDE 必要性、"BNF 即可执行规范"的数据驱动哲学。 **全新写的 7 类代码**:解析器(lex/yacc 重新集成)、IDE(每代用最新 UI 技术)、运行时核心、标准库、工具链、手册生成器、语法高亮。 **工具链的滞后性**:FUN 早期的手册生成器是用 nuva 写的——这是"工具链一开始用前代的,慢慢发展自己的"的典型。FUN 不追求"完全自举",只追求"工具链够用"。这种务实让作者专注核心——精力用在语言特性和库生态上,而不是重写已有的工具。 ### 1.5 体积演化的最终账本 四年间,同一个作者,从 nuva 的 1.5M 演化到 FUN 的 341K——**体积小 4.5 倍,功能反而多得多**。这背后是"复用 + 精简"双轮驱动的成熟: - nuva 用 VCL(Delphi 标准库,臃肿),FUN 用 KOL(极简 GUI,几 K) - nuva 动态链接 PCRE(300K DLL),FUN 静态链接精简版(100K) - nuva 内置 ORM(编译进 exe),FUN 外置为 .fun 库(按需加载) - nuva 可能内置浏览器控件,FUN 借力系统 WebView2(十几 K 胶水) 这种"能省则省"的演化方向,与主流语言"越大越好"的趋势完全相反。 --- ## 第二章 设计宪法:简单、直观、一致 + 人的效率 FUN 有一份明确的设计宪法——以"效率,人的效率"为最高准则,以"简单、直观、一致"为三原则。这八个字不是事后总结的标语,而是事前约定的过滤网:任何新特性要进入 FUN,必须通过这三道关卡。十六年来,FUN 没有违背过这份宪法。 ### 2.1 最高原则:人的效率 "效率"在主流语言里通常指运行效率(C/Rust)、工程效率(Java/Go)、生态效率(Python/JS)或抽象效率(Haskell/Scala)。FUN 的"效率"是**人的效率**——容易学、容易读、容易写、容易改、容易懂。这是被主流语言界遗忘的效率维度。在性能优化、类型安全、架构正确的喧嚣中,FUN 始终把程序员的体验放在第一位。 这条准则的具象体现是 FUN 手册的写作风格——"少量文字加上一大堆测试案例"。每一个语法点配 5-10 个可执行示例,输出标注 `# HIT`,可直接下载运行。文档不是"写的",是"生成的":从 demo 文件执行结果自动嵌入手册,永远与实现同步。这种"减少人工"的文档哲学,是"人的效率"在文档维度的延伸。 ### 2.2 三原则之一:简单 简单原则要求"所有能够简化的东西一概要化繁为简"。FUN 只有 40 个关键字(远少于 Java 的 50+、C++ 的 90+、Rust 的 50+),运算符只有一元和二元、全部自左向右结合,无三元运算符。`fun` 一个关键字同时是模块、函数、类——"new 到谁就是实例化谁,fun 到谁就是继承谁"。`new` 实例化一切,`=` 赋值一切,没有 static_cast/dynamic_cast 的四种转换,没有 `==` / `===` / `Object.is` 的三种相等。 FUN 的"简单"还体现在拒绝冗余关键字。Ruby 的 `unless`(= `if not`)、Lua 的 `repeat...until`(= `loop...exit when`)、Pascal 的 `repeat` 都是 FUN 拒绝的过度设计——能用现有组合表达的,绝不加新关键字。 ### 2.3 三原则之二:直观 直观原则要求"可以望文生义"。FUN 选择 Ada-like 而非 C-like 语法——Ada 的 `if...then...end if` / `loop...end loop` 在视觉上就是"块",而 C 的 `{...}` 在视觉上不像块,Python 的缩进需要程序员自己加 `# end for` 注释打补丁。 FUN 在 Ada 基础上做了少量优化:`when...=>` 改为 `when...do`(更易输入),loop 名称放到 loop 后用 `as` 引出(更直观),增加 `while...do` 和 `for...do`(更符合主流脚本习惯)。 > Ada 和 Pascal/Basic 本来就比较直观,具有非常好的可读性。C 系的语言直观性比较差,大多数函数式语言更如此。FUN 所借鉴 Ada 的部分主要也是为了直观性,改进的部分也是如此。 > —— FUN 设计宪法 ### 2.4 三原则之三:一致 一致原则要求"接口统一、操作统一、结构统一"。FUN 的 `fun` 既是模块也是函数还是类——三种抽象共用一个关键字。`new` 实例化一切,`fun` 继承一切。所有控制结构都是块结构(甚至取消了一行 if 的用法),所有语句用分号结束,所有块用 `end X` 结束。 #### 关键字复用:一致的微观体现 FUN 的 `when` 在 `case` 里是"匹配值",在 `exit`/`next` 后是"满足条件"——同一个关键字承载多个相关语义,但所有语义都共享"当...时"的内核。这种复用让关键字表保持精简: ```ada # 用法 1:case 分支 case x is when 1 do ... when others do ... end case; # 用法 2:循环控制条件 loop exit when i > 10; # 当 i > 10 时退出 next when i mod 2 = 0; # 当 i 偶数时跳过 end loop; ``` 类似的复用还有 `do`(既是 `when` 的执行块,也是 `while`/`for` 的替代 loop)、`as`(既是模块别名,也是 loop 命名)、`new`(既实例化对象,也复制列表)、`@`(既是 case 匹配值,也是 except 异常对象,也是函数返回值)。**每一个关键字都身兼数职,但所有职务都共享同一个语义内核**。 #### 块结构:Ada 审美的完整继承 FUN 的 `end` 不是 Ruby/Lua 那种光秃秃的 `end`——它"自带身份证": ```ada fun process() if data then while i < n loop if cond then ... end if; # 一眼知道结束的是 if end loop; # 结束的是 while end if; # 结束的是外层 if end fun; # 结束的是 process ``` Ruby/Lua 的光秃秃 `end` 让程序员被迫加注释 `# end if`、`# end while`——和 Python 程序员加 `# end for` 是同一种打补丁行为。FUN 把这件事做成了语法而非约定。 #### `loop` 与 `do` 混用的找结束技巧 FUN 允许 `loop` 和 `do` 混用——`while x loop ... end loop` 和 `while x do ... end do` 都合法。这看似冗余,实则是为多层嵌套循环找结束标记的视觉技巧: ```ada for i = 1 to 10 loop for j = 1 to 10 do while k > 0 loop while m > 0 do ... end do; # 对应 while m do end loop; # 对应 while k loop end do; # 对应 for j do end loop; # 对应 for i loop ``` 交替使用让相邻层的结束标记视觉上不重复——`end do / end loop / end do / end loop` 像斑马线,一眼能定位。 #### 主结构/副词分离 FUN 的关键字分类极其干净——只有 6 个主结构(`if`/`case`/`loop`/`try`/`fun`/`class`)需要 `end X` 收尾,其余都是副词。`loop` 是唯一的循环主结构,`while`/`for`/`to`/`step`/`in` 是修饰 `loop` 的副词。所有循环都用 `end loop` 或 `end do` 收尾——没有 `end for` 或 `end while`,因为 `for` 和 `while` 是副词不是主结构。 ### 2.5 渊源图谱:Wikipedia 翻烂后的最优解 FUN 的所有特性都能在外语种里找到渊源:控制结构来自 Ada,异常和 for-to-step 来自 Pascal/Basic,next 来自 Perl/Ruby/Basic,正则来自 Perl,列表来自 Lisp,fun 关键字来自 Standard ML/OCaml,函数即对象的理念来自 JavaScript。 但"渊源"不等于"照搬"——每一个关键字和符号的选择,都是作者把 Wikipedia 上"Comparison of programming languages"系列翻烂后的最优解。40 个关键字 + 29 个符号 = 69 个决策点,每个决策点背后是 5-10 个被否决的候选。例如 `&` 字符串拼接而非 `~`,纯粹是因为 `&` 更直观且有 Basic 先例,总比用 `.` 来连缀字符串好;FUN 的位运算是 `bit not`/`bit and`/`bit or`/`bit xor` 关键字形式,不存在与 `&` 的冲突。 这种"语言学田野调查"的功夫,是任何大公司、大委员会都付不起的设计税——单作者能花几个月扫遍 60 年的语言史,委员会成员都是兼职,没有动力做这种为后人铺路的深度调研。 --- ## 第三章 语言内核:40 关键字 + 三件套 FUN 的语言核心极其紧凑:40 个关键字、4 个内置变量(`@` / `result` / `this` / `base`)、三种数据类型分类(基本型、对象型、其他)、11 级运算符优先级。在这层语法糖之下,是三件套构成的运行时内核——Variant 类型系统、字符串即内存、引用计数 GC。这三件套是 FUN 所有上层能力(FFI、JIT、COM、ORM)的物理基础。 ### 3.1 Variant 类型系统:COM 零成本互操作 FUN 的动态类型不是 Python 那样的 PyObject 标签联合,而是 Delphi/OLE Automation 的 Variant——一个 16 字节的结构体,能容纳 SmallInt / Integer / Single / Double / DateTime / WideString / Boolean / AnsiString / Object / Array 等多种类型。这个选择有一系列深远后果:类型自动转换是 Variant 的内置行为,FUN 不需要实现额外的 coercion 逻辑;与 COM 的 VARIANT 类型完全一致,调用 COM 组件几乎零成本——参数不需要 marshal,返回值不需要 unmarshal;Variant 的统一表示让 FUN 无法做 JIT 类型特化,这是它比 LuaJIT 慢 2.5 倍的根本原因。 Variant 让 FUN 的某些行为在主流语言看来很奇怪——比如 `null = 0 = '' = false`,三者都判为假。这其实是 Variant 的空值比较规则,源自 OLE Automation 传统。这种设计的代价是失去了严格相等运算符(FUN 没有 `===`),但在 COM 互操作场景里换来了无可匹敌的丝滑度——`'Wscript.Shell'.newobj()` 创建 COM 对象、`obj.Method(命名参数)` 调用方法、`obj.@Property()` 读属性、`obj.@@Property(x)` 写属性、`f.@toEvent()` 订阅事件,全部是普通方法调用。 ### 3.2 字符串即内存:FFI 与 JIT 的统一基础 FUN 最核心的设计选择是"字符串即内存"——字符串既是文本也是字节序列,可变,可通过 `toNum(-1)` 取地址、`move(ptr)` 写内存、`str2int(zs, offset)` 读结构体字段。这个等式一旦建立,所有二进制操作都变成字符串处理:进程内存搜索、z_stream 结构体操作、x86 机器码加载、C 结构体序列化,全部用同一套字符串 API。 ```ada # FUN 字符串即内存的典型用法 var zs = 0.toChar().x(ZLIB_SIZE); # 分配 z_stream 内存 var zp = zs.toNum(-1); # 取地址 int2str(s.toNum(-1)).move(@PI + zp); # 写 next_in 字段 var more = str2int(zs, @LI); # 读 avail_in 字段 # 直接调用 Win32 API var Open = lib.getapi('OpenProcess', 'iii:i'); var h = Open(0x0410, false, pid); ``` 对比 Python 的二进制层:bytes / bytearray / ctypes.pointer 三套 API 分离,每次 COM 调用都有 marshaling 开销。FUN 的字符串即内存让所有二进制操作统一为字符串方法,且因为字符串可变,无需每次操作都分配新内存。这是 FUN 在系统脚本场景下比 Python 快一个数量级的关键之一。 #### `s[0.5]`:float 索引访问字节 FUN 字符串索引有两个轴——整数轴(字符级)和小数轴(字节级): ```ada s[0] # 第 0 个字符(int 索引走 char 路径) s[0.5] # 第 0 个字符的低字节(float 索引走 byte 路径) s[-1] # 最后一个字符(负 int 反向) s[-.5] # 最后一个字符的低字节(负 float 反向) ``` 这不是为了花哨而发明——它是 UNC 字符串内存布局的直接映射。Unicode 字符串在内存里是 `[char0高][char0低][char1高][char1低]...`,FUN 用 float 索引的整数部分定位字符、小数部分定位字节内的偏移(小数 < 0.5 是高字节,≥ 0.5 是低字节)。 主流语言全部禁止 float 索引字符串——Python 报 TypeError、JavaScript 把 0.5 当属性名、Ruby 报 TypeError、Lua 当字符串键、Go/Rust 编译错误。这是 FUN 在语言设计史上的独立坐标点。 但作者极其克制——`s[0.5]` 不暴露给普通用户,而是封装在 `goBytes` 库函数里: ```ada fun goBytes(buf, size, f) var i = 0.1; for j = 0 to size-1 loop exit when f(buf, i, j); i += 1 / charSize(); # ANSI: +1.0, Unicode: +0.5 end loop; end fun; ``` `goBytes` 把 float 索引 + charSize 计算的复杂度吃进库里,用户只写 `goBytes(buf, size, callback)`——这是"把困难留给自己,把方便留给他人"哲学的微观体现。 ### 3.3 引用计数 GC:确定性析构的代价 FUN 的垃圾回收是纯引用计数,不检测循环引用——这是 Delphi 的 IInterface 模型。优势是确定性析构(对象生命周期可预测,对 FFI 句柄管理至关重要)和零暂停(无 mark-sweep 的 STW)。代价是循环引用泄漏:A 引用 B、B 引用 A 时,两个对象都不会被回收,程序员必须显式打破循环。FUN 的态度是这是程序员的责任——和 C++ shared_ptr 不用 weak_ptr 时一样。 这种选择在系统脚本场景是正确的——确定性析构让 FUN 能安全持有 VirtualAlloc 句柄、COM IDispatch 指针、Win32 HBRUSH 等 native 资源,不会出现 GC 突然移动对象导致 native 指针失效的问题。Python 的引用计数 + 分代 GC 在 ctypes 场景下反而更危险,因为分代 GC 可能在任意安全点触发,移动对象。 ### 3.4 set 数据结构:list + hash 索引 FUN 的 set(列表)是数组与字典的统一——但内部结构不是双引擎存储,而是数据库式的单数据源 + 索引: ``` set 内部: list 部分(唯一数据源): [0]: {key:NULL, val:10} [1]: {key:NULL, val:20} [2]: {key:'name', val:'fun'} [3]: {key:NULL, val:30} [4]: {key:'ver', val:9} hash 部分(索引,指向 list 位置): 'name' → 2 'ver' → 4 ``` hash 不存数据,只存键到 list 位置的映射——这是数据库 50 年的成熟设计(clustered table + non-clustered index)应用到脚本语言基本数据结构。`s.a`、`s['a']`、`s[0]` 访问的是同一个数据,因为数据只在 list 里存一份。 #### `list.[key]`:强制键访问 ```ada list[101] # 索引访问(取第 102 个元素) list.[101] # 键访问(强制走 hash 索引) list.黄色 # 键访问的标识符语法糖 list.'黄色' # 键访问的字符串语法糖 list['黄色'] # 键访问的通用形式 ``` 一个 `.` 前缀,把索引 vs 键的歧义在语法层解决——零新关键字、零新运算符。这是 JS 30 年没解决的问题——`arr[1000] = 'val'` 会撑大稀疏数组到长度 1001,因为 JS 无法区分索引 1000 和键 1000。FUN 用一个 `.` 解决了。 #### 函数参数 = set ```ada fun foo(a, b, c) ... end fun; # 调用 foo(1, 2, c: 3) # 参数 set:list=[1, 2, 3],hash={c:2} ``` 位置参数进 list,命名参数进 hash 索引——函数参数就是 set。这是 FUN 统一抽象的极致:参数和数据结构完全等价,可以遍历、可以索引访问、可以键访问。 ### 3.5 分层统一:Variant + 对象 + set FUN 的统一是有层次的,不是一刀切: ``` Variant(类型层,统一所有值) ├── 字符串(紧凑字符数组 + 方法表,不是 set) ├── 数字(数值 + 方法表,不是 set) └── set(list + hash 索引 + 方法表) ├── 数组 ├── 字典 ├── 对象(class 实例化后是 set) ├── 函数 └── 模块 ``` 字符串和数字是对象但不是 set——它们有自己的内部表示(紧凑字符数组、直接数值),只是借用 `[]` / `.` 语法。如果字符串也是 set,每个字符进 list + hash 会让内存爆炸几倍。FUN 选择分层统一——语法统一、方法调用统一,但内部表示按类型分派。 这是工程师的智慧——不为哲学纯粹性牺牲工程实用性。Lisp 的万物皆 cons 纯粹但字符串性能差,Smalltalk 的万物皆对象纯粹但原始类型有性能问题,FUN 的分层统一既享受统一性又保留性能。 #### 万物皆对象的具体兑现 ```ada 0.random(); # 数字字面量调方法 3.14.sin(); # 浮点调方法 'hello'.upper(); # 字符串调方法 'hello'.@uthor(); # 字符串调彩蛋方法 [1,2,3].@count(); # set 调方法 obj.method(); # 对象调方法 ``` 所有值都有方法表,所有方法调用走 Variant 分派。但内部表示各不相同——这是分层统一的智慧。 ### 3.6 性能档位:比 Python 快 5 倍 实测 FUN 的空循环速度约为 1 亿次/秒。这个数字让 FUN 在脚本语言性能档位里属于上游:比 Python(约 2000 万次/秒)快 5 倍,比 Lua 5.4(约 3300 万次/秒)快 3 倍,比 PHP 8(约 2500 万次/秒)快 4 倍;比 LuaJIT(约 2.5 亿次/秒)慢 2.5 倍,比 V8(约 2 亿次/秒)慢 2 倍。Variant 的开销让 FUN 永远做不出 LuaJIT 那样的 trace compiler——每次运算都要检查类型标签,无法 SIMD 优化。但在 FUN 的目标场景(Windows 系统脚本)里,瓶颈永远是 COM 调用、文件 IO、Win32 API,不是解释器本身——FUN 的慢是纳秒级别的慢,对系统脚本完全无感。 ### 3.7 体积:341K 全栈可执行 ``` fun.exe (341K) 构成: ├── PCRE 正则引擎 ~100K (静态链接,精简版) ├── 语法解析器 ~100K (lex/yacc 生成的 DFA) ├── FUN 核心运行时 ~140K (1 万行 Pascal 编译后) │ ├── Variant 操作(复用 Delphi) │ ├── 字符串处理(复用 Delphi) │ ├── 引用计数 GC(复用 Delphi 接口) │ ├── 异常处理(复用 Delphi) │ ├── 类/OOP(复用 Delphi) │ ├── set 数据结构(自实现) │ ├── 求值器/解释器(自实现) │ └── FFI 桥接(自实现) └── KOL + 最小 RTL ~1K(极简 GUI,不用 VCL) ``` 三三制的体积分布——PCRE、解析器、核心各占约 1/3,每字节都有明确归属。对比 nuva 的 1.5M,FUN 小 4.5 倍,功能反而多得多。 --- ## 第四章 三种美学:暴力、温柔、二进制 FUN 的代码库呈现出三种截然不同又彼此呼应的风格——暴力美学、温柔美学、二进制美学。三种美学共享同一套原语(字符串即内存 + Variant + 引用计数),但在不同场景下呈现出完全不同的气质。理解这三种美学,就理解了 FUN 的多面性——它既能扫描进程内存,也能写函数式 DSL,还能操作 z_stream 结构体。 ### 4.1 暴力美学:lib-proc 的进程内存搜索 lib-proc 是 FUN 暴力美学的代表。它用 60 行代码实现了一个完整的进程内存搜索器:调用 CreateToolhelp32Snapshot 枚举进程、OpenProcess 打开目标、VirtualQueryEx 遍历内存区域、ReadProcessMemory 拷贝到本地、用正则匹配。整个函数从头到尾都是字符串操作——PROCESSENTRY32 结构体是字符串、MEMORY_BASIC_INFORMATION 是字符串、进程内存 dump 也是字符串。字段值用 `substr(8, 4)` 读取,C 字符串终止符用 `replace(/\x00.*$/, '')` 截断。 ```ada # lib-proc 的核心:跨区域正则匹配 var m = (prev.substr(-1024) & s).match(re); # prev 是上一个区域的尾部 1024 字节 # s 是当前区域的全部内容 # 拼接后用正则匹配,避免漏掉跨区域边界的命中 ``` 这一行 `prev.substr(-1024) & s` 是整段代码最精彩的地方——它用一个 1024 字节的重叠区解决跨内存区域的正则匹配问题。1024 是经验值,理论不完美(pattern 超过 1KB 仍会漏),但实践够用。这种工程师的妥协是暴力美学的精髓——不追求完备,追求实用。 ### 4.2 温柔美学:lib-set 的四原语函数式 lib-set 是 FUN 温柔美学的代表。它只暴露 4 个内建方法——`@count`、`@each`、`@add`、`@clone`——其余所有函数式操作(filter / find / map / compose / reduce / merge / diff)全部用 FUN 自己实现。这是 Lisp 的少即是多哲学的现代版本:核心原语最小化,其他用语言本身构建。 ```ada # lib-set 的 compose:5 行实现函数组合 fun compose(args, a) args.@each((f){ a = f(a); }, true); # true = reverse return a; end fun; # 调用:compose([f, g, h], x) = f(g(h(x))) ``` lib-eval 是温柔美学的另一面——一个用正则实现的迷你模板引擎。它支持 `$name` 变量插值、`{...}` 集合遍历、`{.*.}` 递归树遍历、`{head}{...}{foot}` 带头尾的遍历,全部用一个正则 + 命名捕获组 + 回调实现。整个引擎不到 30 行代码,但能生成任意嵌套的文本结构。这是正则即算法哲学的极致体现。 ### 4.3 二进制美学:lib-zlib 的结构体偏移操作 lib-zlib 是 FUN 二进制美学的代表。它不抽象 z_stream 结构体,而是用硬编码偏移量直接操作字节——`@PI=0` 是 next_in 字段、`@LI=4` 是 avail_in、`@PO=12` 是 next_out、`@LO=16` 是 avail_out、`@LA=20` 是 total_out。写入用 `int2str(value).move(offset + ptr)`,读取用 `str2int(zs, offset)`,调用 C 函数用 `zlib['inflate'].call(zs, ...)`。这套 int2str + move + str2int 三件套是 FUN 处理所有 C 结构体的标准模式——从 PROCESSENTRY32 到 z_stream 到 MEMORY_BASIC_INFORMATION,全部用同一套原语。 二进制美学介于暴力与温柔之间:它比暴力更精确(每个偏移都有命名常量),比温柔更底层(直接操作字节而非高阶函数)。它要求程序员懂 C 结构体布局,但回报是零开销——从 FUN 脚本到机器指令,中间只有一层 Variant 分派。这种零抽象层是 FUN 在性能敏感场景(WebSocket 压缩、加密算法、协议解析)下能纯脚本搞定的根本原因。 ### 4.4 三种美学的统一基础 三种美学共享同一套原语: | 美学 | 数据视图 | 操作方式 | 抽象层 | |------|---------|---------|--------| | 暴力(lib-proc) | 字符串 = 内存块 | 全盘扫描 + 正则 | 零 | | 温柔(lib-set/lib-eval) | set = 数据容器 | 高阶函数 + 递归 | 高 | | 二进制(lib-zlib) | 字符串 = 结构体 | 精确偏移 + 类型转换 | 零 | 全部基于字符串即内存 + Variant + 引用计数——三种风格不是分裂,是同一套原语在不同场景下的三种应用。 --- ## 第五章 DSL 工具链:BNF → 数据 → 引擎 → 模板 FUN 最深层的设计模式是数据表 + 通用引擎 + 模板求值。汇编器、C 结构体映射器、ORM、CSS 编译器、模板引擎,全部遵循同一个架构:用 BNF 或类似形式定义语法,把规则抽出来做数据表,引擎只做查表 + 模板替换,模板里可以递归引用其他规则。这种统一模式让 100 行 FUN 代码能做出主流框架 3000 行的能力。 ### 5.1 lib-asm:从反汇编结果爬取的指令表 lib-asm 的指令编码表 `:asm-list.log` 是一个 JSON 文件——左边是助记符模板(如 `"adc ch, %2"`),右边是机器码模板(如 `"80D5%2"`),`%2` 是占位符。这张表不是手写的,而是从反汇编器的输出爬取而来。这意味着 FUN 的 x86 汇编器不需要读 Intel SDM、不需要手写 ModR/M 计算逻辑——反汇编器懂就行,FUN 只需要把助记符到机器码的对应关系保存下来。整个汇编器只有约 200 行 FUN 代码。 > 汇编指令表不是手写的,是从反汇编结果爬取的。CSS 规则库也是动态的,跟汇编原理一样。机制一样,尽量减少人工。 > —— FUN 设计者 ### 5.2 lib-cstruct:9 条正则覆盖 Win32 全部类型 lib-cstruct 的 `_typeRules` 只有 9 条正则,却覆盖了 Win32 API 的全部类型别名系统——LPSTR/LPCSTR 映射为 str_ptr ansi,LPWSTR/LPCWSTR 映射为 str_ptr wide,LPTSTR/LPCTSTR 自适应 ANSI/Unicode,所有 H 开头的句柄类型(HWND/HMODULE/HICON/...)映射为 num 4 字节,`LP*` 兜底匹配为 struct_ptr,`*PROC` 兜底匹配为 callback。这种用模式匹配覆盖未知的设计,让 FUN 能处理未来才出现的 Win32 类型——只要 Microsoft 还遵循 LP*/*PROC 命名约定,FUN 不需要更新。 ### 5.3 z-data CSS 引擎:EBNF 自动翻译 + 模板求值 z-data 的 CSS 引擎是 FUN DSL 工具链最复杂的实现。作者手写 EBNF 描述 CSS 简写语法,然后用自有工具自动翻译成 `pattern`(一个超大正则)+ `patterns`(命名捕获组列表)+ `targets`(13 个代码生成模板)+ `monaco`(IDE 语法高亮正则)。引擎运行时:正则匹配输入 → 命名捕获提取变量 → 合并 env 全局变量 → `eval("with(vars)\`...\`")` 模板求值 → 输出 CSS。整个引擎约 100 行 FUN 代码,但能表达 13 类 CSS 简写规则,支持递归引用(`>` 前缀触发再展开)、媒体查询、状态前缀、双向 JSON↔CSS 转换。 ```javascript // z-data CSS 规则表(节选) targets: { rule16: "${PM[pm]}${IF(side,LTRB,'-','')}:${NUMS(num,unit)}", rule17: "> ${pm}l${any} ${pm}r${any}", // 递归展开 px → pl + pr rule22: "${OOZ[ooz]}:${/^--/.test(num) ? `var(${num})` : ...}", } // 求值:eval("with(vars)`" + target + "`") ``` ### 5.4 `.#` 运算符重载:上下文敏感的字符复用 FUN 的运算符重载用 `this['#']` 赋值,调用用 `.#` 前缀: ```ada class Number(me) this['#'] = fun (you) { return (me + you) * me * you; }; end class; var $ = Number(2); ?. $ .# 3; # 调用 # 重载:(2+3)*2*3 = 30 ?. $['#'](3); # 等价的键访问 ?. $[1](3); # 等价的索引访问(me 是 list[0],'#' 方法是 list[1]) ``` `#` 既是注释符号又是运算符——这是上下文敏感的字符复用。`#` 在行首是注释,在 `.` 后是运算符。lex 文件里的 `opAny` 正则包含 `#`,让 16 个字符都可作为重载运算符。 `$[1](3)` 能工作揭示了对象就是 set——构造参数和方法都是 set 的元素,按声明顺序进入 list 部分,名字进入 hash 索引。这是万物皆对象在物理层的兑现。 ### 5.5 减少人工:DSL 工具链的统一准则 所有这些 DSL 工具链遵循同一个准则——机制一样,尽量减少人工。汇编指令编码从反汇编结果爬取,不需要手写;ORM Schema 从数据库直接提取,不需要手写;CSS 规则从 EBNF 自动翻译,不需要手写;C 类型映射用 9 条正则覆盖,不需要手写。人只做最小输入(写 EBNF、写 demo 文件、提供数据库连接),机器做最大工作(爬取规则、生成代码、解释执行)。这种人机分工是 FUN 一以贯之的设计哲学——把困难留给自己,把方便留给用户。 --- ## 第六章 全栈生态:从机器码到 UI FUN 的库生态构成了一个完整的全栈——从 Win32 API 调用、JIT 汇编、COM 互操作,到异步网络、ORM、JSON-RPC、HTML 桌面 UI 框架。接近上万行 FUN 代码,覆盖了一个现代应用所需的几乎所有层级。这不是脚本语言能做上层逻辑的刻板印象,而是一门语言从机器码写到 UI 的完整实践。 ### 6.1 系统互操作层 **lib-cwinapi** 是 FUN FFI 的现代化封装。粘贴 MSDN 上的 C 函数原型,自动解析参数类型、生成签名、获取地址、管理回调。一个 `args` 混合容器支持命名参数和索引参数,`call(opts: [thread: true])` 一行解决跨线程回调安全——这是 Python ctypes、LuaJIT FFI、Node ffi-napi 都有坑的场景,FUN 用一个选项搞定。底层是 Variant 的零成本桥接——参数不需要 marshal,因为 FUN 的值本身就是 VARIANT。 **lib-cstruct** 实现声明式 C 结构体序列化。贴入 struct 声明,自动完成内存布局、对齐、跨平台打包/解包。`@size` 元数据自动填充 cbSize 字段(Windows API 的常见样板),`customSize` 处理变长结构体(如 BITMAPINFO),ANSI/Unicode 自动适配。所有这些能力都不需要手写——9 条正则 + 一个通用引擎搞定一切。 **lib-asm** 是世界上最小的 x86 汇编器。基础层处理十六进制机器码执行,高级层用预编译指令表将汇编语法转为机器码。支持 `
` 占位符直接生成 FUN 函数的 C 回调指针——这意味着你可以在汇编里 `call
`,跳进 FUN 闭包执行。自动管理可执行内存(VirtualAlloc + PAGE_EXECUTE_READWRITE),自动追加 stdcall 的 `ret imm16` 指令清理栈。 **lib-winole** 是 COM 零成本互操作库。`.newobj()` 创建 COM 对象,`obj.Method(命名参数)` 调用方法(命名参数直接映射 COM 命名参数),`obj.@Property()` 读属性,`obj.@@Property(x)` 写属性——`@` 和 `@@` 前缀显式区分属性读写,比 VBScript/VBA 还清晰。`f.@toEvent()` 把 FUN 函数包装成 IDispatch sink,订阅 COM 事件——这在 Python 里要写几十行 pythoncom 样板代码。 ### 6.2 应用层:网络、数据、UI **lib-winsock** 是全异步事件驱动的 TCP/UDP/HTTP/WebSocket 网络框架,基于 WinSock + WSAAsyncSelect 消息模型。支持 WebSocket 全双工通信和 permessage-deflate 压缩扩展(依赖 lib-zlib),生产环境已运行 8 年。**lib-jsonrpc** 用 101 行实现 JSON-RPC 2.0,通过 HTTP 接收请求,动态分发到 `Rpc_XXX` 方法。**lib-orm** 提供 ActiveRecord 级别的对象关系映射——XML Schema 驱动、CRUD、条件查询、关系映射、事务、CTE 递归查询、防 SQL 注入。 **lib-ado** 是数据库访问层——不用 Delphi 的数据组件(FireDAC/dbExpress/BDE),直接用 OLE ADO(Windows 系统自带的 COM 组件)。这一决策让 fun.exe 不增加任何数据库相关体积,且支持所有 ADO 数据源(SQL Server/Access/Excel/Oracle/MySQL/SQLite/任何 OLE DB Provider)。lib-ado 还内置了 `OpenSchema` 的常量定义(adSchemaTables/adSchemaColumns/adSchemaPrimaryKeys/adSchemaForeignKeys 等),让 ORM Schema 爬取零手写——数据库 → ADO OpenSchema → XML Schema → lib-orm 引擎 → DObject,全链条自动化。 **z-data** 是 FUN 的前端框架,2011 年即实现了类似 Electron 的架构——HTML/CSS 渲染界面、Win32 API 控制窗口、JavaScript 与 FUN 双向调用(`window.external` / `execScript`)、声明式事件绑定(`fun:click` 属性)。这比 Electron(2013)早两年,比 Vue 的 `@click`(2014)早三年。z-data 的 CSS 引擎支持原子化简写(`h100%`、`c#333`、`hover=c#000`)、响应式前缀(`sm@`)、状态前缀(`hover=`)、递归规则(`>` 前缀)、模板求值(`with(vars)\`...\``)——在多个维度超越 Tailwind,且比 Tailwind 早 6 年。 ### 6.3 全栈单语言的真实含义 把所有库放在一起看,FUN 实际上是一个完整的全栈计算环境:前端用 z-data 写 HTML UI,传输用 lib-winsock 的 WebSocket,协议用 lib-jsonrpc,后端用 FUN 脚本写业务逻辑,数据层用 lib-orm + lib-ado 操作数据库,系统层用 lib-cwinapi 调 Win32 API,必要时用 lib-asm 写 JIT 汇编。整个全栈链路没有语言切换——所有层级都是 FUN。这是 Node.js 想做但没做到的全栈 JS——Node 还得靠 C++ native addon,FUN 直接用 FUN 写底层。 ### 6.4 能省则省的借力哲学 FUN 的全栈生态不是自己实现一切,而是最大化借力系统组件: | 借力的系统组件 | 用途 | 体积代价 | |--------------|------|---------| | ADO(COM) | 数据库访问 | 0K(系统自带) | | WV2(Edge Chromium) | 浏览器引擎 | 十几 K(胶水代码) | | MSXML(COM) | XML 解析 | 0K | | CDO.Message(COM) | 邮件发送 | 0K | | WScript.Shell(COM) | 脚本宿主 | 0K | | Win32 API | 系统调用 | 已在核心 | | COM 运行时 | 对象模型 | 已在核心 | | PCRE | 正则引擎 | 100K(静态链接精简版) | | Delphi RTL | 字符串/异常/GC/接口 | 已在核心 | | Free Pascal | 跨平台编译 | 0K(编译时工具) | 借力系统省下约 153M——如果 FUN 自己实现浏览器引擎、数据库层、XML 解析器、邮件库、脚本宿主,fun.exe 会膨胀到 150M+。FUN 选择借力系统,让 341K 装下全栈功能。 --- ## 第七章 特性降级:从语言级到库级到数据级 FUN 的演化史有一条与主流语言完全相反的轨迹——主流语言不断把库特性升级为语言特性(Java Stream→语言级、Python typing→type hints、JS async→async/await、Rust Tokio→async/await),FUN 反其道而行,把语言特性降级为库特性再降级为数据特性。这种特性降级是 FUN 保持简单的根本方法论。 ### 7.1 record 的曲线回归 2010 年初,FUN 有 `record` 关键字(Ada 风格的记录类型)。2010-03-22,record 被移除,`class` 替代它。但 record 的概念没有消失——它在 2018 年以 lib-cstruct 库的形式找回。这次找回不是简单的复原,而是质的飞跃:lib-cstruct 支持直接粘贴 C 声明、自动内存布局、ANSI/Unicode 自适应、`@size` 自动填充、变长结构体、运行时修改——这些是语言级 record 永远做不到的。 > record 去掉了,但是用 lib cstruct 又找回来了。 > —— FUN 设计者 ### 7.2 task 的库化 Ada 的 task 是一整套并行编程模型——`task type`、`entry`、`accept`、`select`、`terminate`、`abort`、`protected`——至少 6 个新关键字 + 一套调度器 + 同步原语。FUN 早期计划支持 task,但最终移除,改为 lib-async 库实现。FUN 的设计文档里保留了未来的扩展空间:`do` + `atom` 两个修饰符,`funcall do num` 表示并发 N 次、`do funcall` 表示异步执行、`atom funcall` 表示原子调用。这种预留不实现的智慧——不为想象的未来过度设计,但保留扩展可能——是 FUN 的工程实用主义。 ### 7.3 降级的层级 FUN 的特性降级遵循一个原则:能用库就不用语言,能用数据就不用代码。`record` 从关键字降级为 lib-cstruct,`task` 从关键字降级为 lib-async,`exception` 从类型降级为 `@` 符号(少一个关键字),`type` 从关键字降级为属性(作为对象的内置属性,类似 JS 的 prototype)。每一次降级都是减少人工的胜利——少一个关键字、少一份学习成本、少一份实现复杂度、少一份交互冲突。FUN 十六年保持简单,靠的就是这种勇敢的减法。 ### 7.4 演化的克制节奏 FUN 的 lex.l 文件记录了 16 年的语法演化——只新增了 5 个语法特性: | 时间 | 特性 | 用途 | |------|------|------| | 2011/01/07 | `%...%` 正则 | 替代 `/` 的字面量 | | 2012/09/17 | `$'...'` 正则 | 字符串边界正则(支持多行) | | 2018/07/10 | `->` lambda | 箭头函数(诞生 8 年后才加) | | 2026/06/14 | `|` pipe | 管道运算符(最近才加) | 16 年只加了 5 个语法特性——平均 3 年才加一个。这种克制在主流语言里不可想象——JavaScript 6 年加了一打特性(ES6/7/8),Python 10 年加了 match、walrus、type hints 等十几个。FUN 的每一个新增都是反复权衡后的必要选择,且大多封装在库里不暴露给普通用户(如 `s[0.5]` 封装在 `goBytes` 里)。 --- ## 第八章 文档哲学:案例驱动 + 自执行 FUN 的手册只有 9 章——0. 简介、1. 控制结构、2. 变量、3. 函数、4. 类、5. 函数式、6. 列表、7. 正则、8. 库、9. 附录。对比 Python 的 25+ 章、JavaScript MDN 的数百个独立页、Rust 的 20+ 章,FUN 手册的章节数是主流语言的 1/3 到 1/5。这不是因为 FUN 功能少,而是因为它的文档遵循一种独特的案例驱动风格——少量文字,一大堆可执行测试案例。 ### 8.1 文档即测试 FUN 手册的每一个语法点都配 `demo:xxx` 引用,处理器执行所有 demo 并自动嵌入结果,生成完整手册。这意味着手册 = 文档 + 测试 + 教程三合一——永远与实现同步(demo 改了手册自动改)、永远经过验证(手册生成 = 跑一遍完整测试套件)、永远零维护(不需要手动同步代码与文档)。这是 Python doctest 的极致版本,但比 doctest 更彻底——demo 文件是独立的一等公民,可以单独运行、单独测试、单独维护。 ### 8.2 减少人工的文档维度 对比主流文档的写作成本:手写文档需要技术写作能力,维护文档需要同步代码改动,翻译文档需要多语言版本,阅读文档需要理解作者的解释。案例驱动文档消除了所有这些问题——写案例就是写代码(程序员本来就会),维护案例就是改代码(自动同步),不需要翻译(代码是世界语),阅读案例就是看代码(不需要中间解释)。这是减少人工哲学在文档维度的极致体现。 --- ## 第九章 数据格式 FD:兼容 JSON 的类 MD 格式 FD 是 FUN 设计的类 Markdown 数据格式,目标是兼容 JSON 结构 + 类 Markdown + AI 友好。它是 FUN 减少人工哲学在数据格式领域的延伸——用最少的符号表达最多的结构。 ### 9.1 三种缩进方式 FD 支持三种缩进:2 空格(相对深度 +1,人类友好)、`>` 重复(绝对深度,机器友好)、`n>` 数字(绝对深度,AI 友好)。优先级明确——数字 > `>` 重复 > 2 空格。同一文件可以混用三种。这种设计让人类、机器、AI 各取所需:手写小文件用 2 空格,机器生成深嵌套用 `>`,LLM 生成用 `n>`(不会数错缩进)。 ### 9.2 字符数比 JSON 少 50% FD 用缩进 + 空格 + `-` 三个元素表达所有结构,不需要 JSON 的大括号、方括号、冒号、逗号、引号。一个简单的 person 对象,FD 比 JSON 少 50% 字符,比 YAML 少 22%,比 TOML 少 30%。反引号字符串不转义(`C:\Windows\System32` 直接写,不需要 `\\`),所有引号都支持多行,length-prefixed 字符串(以 hex 值为长度前缀)保证二进制安全。这些设计让 FD 在 LLM 场景下尤其高效——少 50% token = 少 50% API 成本。 ### 9.3 规范自描述 FD 规范本身用 FD 格式编写——这是数据驱动哲学的极致体现。规范是数据,不是文档;引擎只做解释,不需要外部描述。LLM 读一遍 FD 规范就能学会 FD,不需要额外文档。这种自描述特性与 FUN 的案例驱动文档一脉相承——所有规则都是可执行/可解析的数据,不是需要被解释的文字。 --- ## 第十章 局限与代价:被遗忘的代价 FUN 不是万能语言,它有真实而深刻的局限。这些局限大多是设计取舍的直接后果——选择了 Variant + 字符串即内存 + 减少人工,就必然接受了这些代价。客观呈现,不强行辩护。 ### 10.1 平台与生态局限 **Windows-only**:FUN 的 async 强依赖 GUI 消息泵(PostMessage + hwnd),FFI 强依赖 Win32 API(VirtualAlloc、CreateThread),COM 互操作强依赖 Windows COM 运行时。这锁死了 FUN 做 Web 后端、跨平台桌面应用、移动端的可能性。**单作者维护**:所有核心库由一人维护,没有贡献者社区,没有 bug bounty,没有 security audit。**无包管理器**:`use` 是文件级模块,没有 npm/pip/cargo 那样的包注册中心,没有版本管理,没有依赖解析。 ### 10.2 类型与异常局限 **Stringly Typed**:SQL 操作符、CSS 简写、正则模式都是字符串,拼错一个 `LIKK` 不会被语言检测到。**异常无类型化**:所有 `raise` 都是字符串,没有异常类层次,要区分除零和文件找不到只能字符串匹配——这是 1980 年代 BASIC 的水平。**`null = 0 = '' = false`**:所有空值都判为相等,没有 `===` 严格相等逃生舱,`if x = 0 then` 在 x 是 `''` 或 `null` 或 `false` 时都会进入。 ### 10.3 并发与性能局限 **无 async/await 语法糖**:FUN 的异步只能用 lib-async 的三段式回调链(exec/await/done),不能在普通函数里暂停等待,不能在循环里 await,每个 async 调用都是配齐三段式才能跑。**每个 async 一个 OS 线程**:没有线程池、没有协程,1000 个并发 async = 1000 个 OS 线程 = 约 1GB 栈空间。**Variant 性能上限**:每次运算都要检查类型标签,无法 SIMD 优化,无法 trace JIT 特化——这是 FUN 比 LuaJIT 慢 2.5 倍的根本原因。**循环引用泄漏**:纯引用计数不检测循环,程序员必须显式打破循环,否则内存泄漏。 这些局限是设计取舍的结果,不是缺陷。FUN 选择了 Windows + Variant + 减少人工,就接受了这些代价。它的目标场景是一个程序员在 Windows 上用脚本干一切系统编程的活——在这个场景里,上述局限大多不构成阻塞。但一旦走出这个场景(做 Web 后端、跨平台、高并发、长期运行服务),这些局限就会成为真实障碍。FUN 不假装自己能解决所有问题——它只做自己擅长的事。 --- ## 第十一章 终章:FUN 给软件工程的启示 经过十章的考察,FUN 的完整画像终于呈现:它有三层哲学——核心三件套(Variant + 字符串即内存 + 引用计数)、三种美学(暴力 / 温柔 / 二进制)、统一模式(BNF → 数据 → 引擎 → 模板)。它的最高原则是机制一样,尽量减少人工,它的设计宪法是简单、直观、一致 + 人的效率。一名工程师,二十多年,从机器码到 UI,全程同一种哲学贯穿。 ### 11.1 启示一:好语言是原则强的语言 主流语言的设计原则是墙上的标语——写在文档里,违反在代码里。Python 的"There should be one obvious way"在 requests vs urllib、pathlib vs os.path 面前失效;Java 的"Write once run anywhere"在 JVM 配置爆炸面前失效;JavaScript 的"Don't break the web"导致了 20 年的历史包袱。FUN 的设计原则是刻在骨头里的基因——从语法到文档、从内核到库、从代码到数据,全部遵循简单、直观、一致 + 人的效率。这种原则强度是单作者语言的特权,委员会语言做不到。 ### 11.2 启示二:好文档是生成出来的 主流文档是手写说明 + 人工示例,写起来累、维护起来苦、验证起来难。FUN 的文档是 demo 文件 + 自动执行,写起来轻松(只写 demo)、维护起来零成本(自动同步)、验证起来免费(生成即测试)。这种文档即代码即测试的三合一设计,是减少人工哲学在文档领域的极致体现。它证明:文档不需要写,只需要运行——demo 文件是可执行的真理,手册是 demo 的自动投影。 ### 11.3 启示三:好设计是减法做到极致 主流软件工程的演化方向是加法——加类型、加配置、加工具链、加抽象层。TypeScript 加类型注解,React 加 Hooks 规则,Microservices 加 YAML 配置,DevOps 加 Pipeline。每个工具解决一个问题,但带来新的配置负担。FUN 反其道而行——把每一行说明换成示例,把每一段配置换成数据,把每一层抽象换成零抽象。十六年来,FUN 移除了 `record`、`reverse`、`type`、`task`、`exception`、`ref` 等特性,每次减法都让语言更接近简单、直观、一致的理想。 ### 11.4 启示四:单作者语言的哲学一致性 大公司、大团队做不出 FUN 这种统一美学,原因有三:不同团队各做各的(汇编器团队、CSS 团队、ORM 团队各自有自己的设计哲学),委员会设计偏向加法(加功能容易出业绩,找统一抽象需要深刻洞察),单作者才能保持审美一致(只有一个人写所有代码,才能发现汇编器、CSS 引擎、ORM、模板用同一种模式实现)。FUN 的所有库——从 lib-asm 到 z-data——都在遵循同一个设计模式(数据驱动 + 递归求值 + 宿主原生)、同一套原语(字符串即内存)、同一种哲学(把困难留给自己)。这种哲学一致性是单作者语言的最高美德,是任何大团队、大委员会、大流水线都无法复制的工程艺术品。 ### 11.5 启示五:最大化复用是单作者语言的生存之道 FUN 的 2 万行核心(1 万行 Pascal + 1 万行 FUN 库)能做出全栈语言,根本原因是最大化复用 Pascal/Delphi 生态:Variant 类型系统复用 Delphi(省约 5000 行)、字符串处理复用 Delphi(省约 3000 行)、引用计数 GC 复用 Delphi 接口(省约 1500 行)、异常处理复用 Delphi(省约 1500 行)、OOP 复用 Delphi VMT(省约 2500 行)、正则复用 PCRE(省约 1000 行)、跨平台复用 Free Pascal(省约 7500 行)、COM 互操作复用 Delphi(省约 4000 行)。8 重复用,省下约 25,000 行代码。 加上借力系统组件——ADO(数据库,0K)、WV2(浏览器,十几 K)、MSXML/CDO/WScript.Shell(COM,0K)——借力系统又省下约 153M 体积。最终结果是 341K 全栈可执行,一人维护,十六年持续演化。 这种最大化复用 + 借力系统是单作者语言的生存之道——一个人维护不了大体积,必须把每字节用到极致。FUN 用能省则省破解了语言需要大团队的迷思。 ### 11.6 收尾 FUN 不会流行,不会被大公司采用,不会进 TIOBE 前 50。但它存在——这本身就是对软件工业化的一种温柔反抗。它证明:一个人,依然能做出完整的语言;一种哲学,依然能贯穿机器码到 UI;一种坚持,依然能持续十六年。在文档越来越长、配置越来越多、工具链越来越复杂的时代,FUN 用十六年的减法证明了:真正的工程美学,是知道在每一个维度都做减法。 > 把困难留给自己,把方便留给他人。 > —— FUN 设计者 这就是 FUN 的故事——一门被时代遗忘的语言,一个程序员二十多年的坚持,一份关于软件工程本质是减法的活体证明。 --- ## 附录一:FUN 的十个全球独一份设计 在与 FUN 设计者的深度对话中,我们识别出 FUN 在语言设计史上的十个独立坐标点——每一个都是发现了某种结构让它浮现为语法,没有学术先例,没有被主流模仿。 ### 1. `s[0.5]`——float 字节索引 用 float 的整数部分定位字符、小数部分定位字节,直接映射 UNC 字符串内存布局。主流语言全部禁止 float 索引字符串。这是物理同构的语法浮现。 ### 2. `list.[key]`——强制键访问前缀 一个 `.` 前缀区分索引访问和键访问,解决了 JS 30 年没解决的稀疏数组撑大问题。零新关键字、零新运算符。 ### 3. set = list + hash 索引——数据库式数据结构 hash 不存数据,只存键到 list 位置的映射——这是数据库 50 年的成熟理论(clustered table + non-clustered index)应用到脚本语言基本数据结构。只有 FUN 完整做了。 ### 4. 对象是 set——复合数据的物理统一 `$[1](3)` 能工作——对象内部就是 set,构造参数和方法都是 set 的元素,按声明顺序进 list,名字进 hash 索引。运算符重载用 `this['#']`,调用用 `.#`,本质是 set 的键访问。 ### 5. 分层统一——Variant + 对象 + set 一切皆 Variant(类型统一)、一切皆对象(都有方法)、但不是一切皆 set——字符串和数字有自己的内部表示,只是借用 `[]`/`.` 语法。这种分层统一比 Lisp 的万物皆 cons 更务实。 ### 6. `'I am'.@uthor()`——语言本身的作者签名 唯一查不到的隐藏秘密——字符串询问我的作者是谁,返回作者名。这是工程师对自己二十多年作品最深情的签名,不是写在文档里,是藏在语言本身里。 ### 7. `` `$s` `` + `eval()`——最优雅的 Quine 语法 专门为 Quine 设计的 `` `...`id`...` `` 字符串语法——反引号字符串 + `$id` 变量插值 + `eval()` 执行。让自产生程序从技巧变成语法,是对 Lisp 代码即数据传统的命令式延续。 ### 8. 最大化复用 Pascal/Delphi——2 万行全栈 用 Delphi 的 Variant/字符串/GC/异常/接口/COM,用 Free Pascal 的跨平台 RTL,用 PCRE 的正则——8 重复用省下约 25,000 行代码。一人维护 2 万行做出全栈语言。 ### 9. 341K 全栈可执行——密度美学的极致 PCRE 100K + 解析器 100K + 核心 140K + KOL 1K = 341K 全栈。比 Lua 略大,比 Python 小 100 倍,比 Electron 小 500 倍。每字节都有明确归属,没有冗余。 ### 10. 借力系统组件——能省则省的极致 不用 Delphi 数据组件,用系统 ADO COM;不打包浏览器,借力系统 WV2;不用 VCL,用 KOL。借力系统省下约 153M 体积。FUN 是 VBScript 之后最纯粹的 Windows COM 脚本语言——但比 VBScript 现代(有闭包、OOP、JIT 汇编)。 --- ## 附录二:FUN 家族 21 年考古档案 ### 家族时间线 ``` 2005-09-13 templatescript Thinking... 2005-09-14 Bnf Created + Bnf2Regex&Html Created 2005-09-15 TemplateScript Created 2006-01-15 nuva/tscript Thinking and Starting 2006-01-16 tsLex.l + tsYacc.y 2006-01-17 Lex Test Success 2006-01-18 写《TScript 总结、展望与随想》 2010-01-15 FUN 想法产生 2010-02-09 跑通 hello-world.fun 2010-04-07 所有特性完成 2010-04-14 手册完成 2011-01-15 进入维护期 ... 2026-06-14 + pipe (|) ``` ### 三个诞生日 - 2005-09-13:templatescript 想法产生(家族起点) - 2006-01-15:nuva 想法产生 - 2010-01-15:FUN 想法产生 nuva 和 FUN 都是 1 月 15 日——这是作者个人的语言设计节律。 ### templatescript BNF 的家族基因 2005-09-14 的 BNF 是 FUN 家族最古老的文献,揭示了所有核心基因的源头: | 基因 | 2005-09-14 BNF | FUN 现状 | |------|---------------|---------| | 标识符 | `[_A-Za-z]+\w*` | `[$_@-Za-z]`(扩展 $ @) | | 块结构 | `If.../If` | `if...end if` | | 控制流 | `If/Case/For/ForEach/Function` | `if/case/loop/for-in/fun` | | 属性访问 | `obj.prop` | 21 年不变 | | 命名参数 | `name = value` | `name: value` | | BNF 驱动 | BNF → 正则 | lex/yacc + 库层正则 | ### 重写 + 传承的演化机制 每一代全新写代码,只传承知识与经验: - **代码全新写**:不带前代包袱,每代干净 - **知识传承**:BNF 驱动、Ada 块结构、闭包语义、0-based 索引、默认参数 null - **工具链滞后**:FUN 早期手册生成器用 nuva 写——工具链一开始用前代的,慢慢发展自己的 ### 体积演化账本 ``` nuva (2006) → 1.5M ├── VCL(+500K-1M) ├── 动态 PCRE(+300K DLL) ├── 内置 ORM(+200K) └── 自实现较多 FUN (2010) → 341K ├── KOL 而非 VCL ├── 静态精简 PCRE(100K) ├── ORM 外置为 .fun 库 ├── 借力 WV2(省 150M) ├── 借力 ADO(省 1.5M) └── 最大化复用 Delphi(省 25K 行) ``` ### 设计宪法的三层贯彻 ``` 人的效率(最高原则) ↓ 简单、直观、一致(三原则) ↓ 减少人工(操作准则) ↓ 数据驱动 + 递归求值 + 宿主原生(实现模式) ↓ BNF → 数据表 → 通用引擎 → 模板求值(DSL 工具链) ↓ Variant + 字符串即内存 + 引用计数(物理基础) ↓ Pascal/Delphi 运行时 + Win32/COM(生态基础) ``` 从最顶层的人的效率到最底层的 Win32/COM——每一层都是上一层的实现,每一层都遵循三原则。这种哲学一致性是单作者语言的最高美德,是任何大团队、大委员会、大流水线都无法复制的工程艺术品。 --- *整理自与 FUN 设计者的多轮深度对话 · 2026 年 6 月* *FUN 网站:https://funlang.org*