Skip to content

设计原则 ​

语言哲学说明 Norm 重视什么,本页定义一个功能怎样才能进入语言、标准库或官方工具链。它们是面向演进的工程准则,不是语法功能列表。

1. 为可观察行为建立唯一规则 ​

同一种语法在不同上下文中应保持同一种含义。名称解析、求值顺序、复制、相等、nullability 和失败传播不能由框架或后端临时重定义。

当一项能力需要特殊行为时,应先确定它属于语言、标准库还是应用平台,并在对应层建立唯一契约。实现优化和宿主 adapter 只能实现这份契约,不能成为第二套语义来源。

2. 用不同类型表达不同数据关系 ​

结构数据、对象身份和存储位置是三种不同关系,因此分别使用 value、class 和 ref<T>。普通缺失、业务结果和系统异常同样分开表达。

如果一个抽象要求调用方根据文档猜测“这个对象究竟会不会共享”或“这个错误究竟会不会抛出”,说明类型边界还不完整。

3. 信息留在最需要它的位置 ​

声明处决定长期契约,调用点保留理解当前操作所需的信息。

  • 普通类型非空,允许 null 时写 ?;
  • 多个参数保留标签,参数名属于公开调用约定;
  • 控制流表达式在产生结果的位置写 break;
  • extension 必须显式声明和导入;
  • 反射使用 reified 类型参数进入,不接收类型名字符串。

显式信息应当稳定且有辨识度。重复类型、无意义包装和可以可靠推断的局部细节不属于这个原则。

4. 高级能力仍然服从普通规则 ​

新增能力不应旁路名称解析、类型检查、可见性和求值顺序。

Extension function 复用顶层函数和重载解析;Annotation 是普通 aggregate,并通过名义策略 interface 获得目标、保留和生命周期;结构序列化读取 Core 类型与字段 metadata;模块描述本身也是经过 Norm 编译和求值的程序。

如果一项能力只能依靠宏 DSL、classpath 扫描、字符串方法名或隐式全局注册才能工作,应先重新设计底层语言边界。

5. 默认行为服务长期应用代码 ​

Norm 优先考虑后端服务、业务系统、工具和桌面应用,而不是内核、硬实时或极端类型级编程。

因此语言选择垃圾回收、非空默认、名义类型和确定赋值;标准库选择有界读取、确定性资源关闭和类型化领域异常;工具链选择自包含 CLI 与一致的编辑器语义。

默认值应让普通代码安全、清楚,少数特殊需求再通过显式 API 进入。

6. 语言、标准库、平台和工具链保持分层 ​

层次职责
语言类型、值、调用、控制流和求值语义
标准库集合、I/O、时间、文件、HTTP、serialization 等通用 API
应用平台Web server、数据库、配置、依赖注入和部署模型
工具链编译、Core、LSP、测试、打包和发布

下层不应知道上层框架。HTTP 核心 body 使用 Bytes,JSON 组合位于独立入口;反射提供结构能力,serialization 再决定映射规则。这类分层让能力可以组合,而不会把某个格式或框架写进语言。

7. 一个事实只定义一次 ​

可生成或派生的信息不手工维护第二份。

内建 ABI 由声明式 schema 生成;编译器和 LSP 读取同一 SemanticModel;JSON、XML 与 YAML 复用同一 serialization shape;网站 base、manifest 和公开 URL 由同一配置派生。

文档负责解释概念、边界和导航。实现细节已经由代码唯一确定时,文档链接到源码或 API,而不是复制一份容易漂移的逻辑。

8. 优化位于语义之下 ​

Core canonicalization、definition store、Truffle specialization、结构共享和运行时打包都可以改变程序的表示与执行方式,但不能改变可观察行为。

后端必须消费已解析的 canonical Core,不能重新执行语言级重载或类型推断。缓存以强类型 identity 和真实依赖作为失效边界,不能用文件时间或字符串 key 模拟语义身份。

功能进入项目前的检查 ​

一项新能力至少应能回答:

  1. 它解决的是语言问题、通用系统能力还是应用框架问题?
  2. 调用者能否从类型和源码看出共享、失败与生命周期?
  3. 它是否复用现有名称解析、类型、调用和资源协议?
  4. 它会不会建立新的 metadata、配置或执行真相源?
  5. 编译器、LSP、测试和正式发行包是否观察同一行为?
  6. 删除旧路径后,整体概念数是否仍然可控?

不能清楚回答这些问题时,继续增加 API 通常只会把结构性问题推迟到未来。

下一篇:设计白皮书。

Norm 0.25