理论核心
提示
版权声明
- 文章作者: flytreeleft - flytreeleft@crazydan.org
- 文章链接: https://nop.crazydan.io/implement/theory
- 版权声明: 本文章采用许可协议《署名 4.0 国际 (CC BY 4.0)》,转载或商用请注明文章来源及作者信息。
注意,此文为思维碎片,还未整理成文。
- 信息空间中最有效的生产方式不是组装,而是掌握和制定运算规则
- Nop 不仅仅是一种具体的开发框架,它更是一种新的思维方式。 Nop 解决问题的方式是先定义一个 DSL 语言去建立局部的描述框架, 相当于是在广袤的模型空间中建立一个个的据点, 然后不同的模型之间再建立自动转换的技术路径, 通过完全自动化的推理就可以在模型空间中自由通行
差量 - Delta
- 差量在本质上描述的是系统的变化过程,是一种向系统施加变化的工具,并且具备时间回溯能力
- 应用开发不能只有增量,还必须有减量,增量和减量合称为差量
- 通过差量可以支持相似可复用,而不再是相同可复用。 可以充分利用相似性以最大限度地提高系统的可复用性,并保证支持对差异化的能力开发
- 可最大限度地降低系统熵增的速率,保证系统的不断演化
- 定制化代码相当于是一种针对基础产品的 Delta 修正
- 差量 Delta 应该是架构设计中的第一类(First Class)概念。 这样它才可以被独立识别、管理、存放。 定制化代码应该和基础产品代码在物理层面分离,单独进行版本管理
- 全量是差量的一个特例
- Delta 可以脱离基础产品,独立的对多个 Delta 进行合并运算, 将多个 Delta 打包为一个整体
- Delta 必须包含逆元,这样才能实现对原系统的剪裁。 Delta 应该是新增、修改、删除的混合体
- 系统需具备一个稳定的定位坐标系。
dA 与 A 分离之后,存放到独立存在的 Delta 中,那么它必然保留了某种定位坐标,
只有这样,当 Delta 与 X 结合的时候,才可以重新找到原始的结构 A,然后与 A 相结合
- 坐标系是指能够唯一定位到各个模型具体属性的方式方法
- Delta 可以理解为应用在不可变数据或不可变逻辑之上的一种变更机制, 也就是,被修改的数据或函数是不可变的,若要对其进行调整, 就需要通过差量技术层层记录针对上一层的差量,再统一进行合并, 合并后的结果便是最终的状态
状态驱动 - State Driven
- 关注的重点是系统的状态以及状态之间的差异,而不再是传统的基于动作概念的 API 调用和事件监听
- 从状态 A 迁移到状态 B,无论经过什么路径,最终得到的结果都是一样的,是路径无关的。 摆脱了路径依赖极大简化了我们对系统的认知
- 从状态 A 迁移到状态 B 有多条可行的路径,在这些路径中按照成本或者收益原则选择其一, 这就是所谓的优化
注:状态的迁移,才是更加符合现实世界的内在驱动模式。
DSL
- 属于编译期技术方案,需要将 DSL 编译后才能得到可执行的逻辑
- 通过 DSL 提取并抽象应用功能模块
- 一个应用就是一片 DSL 森林
- 不同的层级有不同的 DSL(多级抽象),从上至下,DSL 的细节会越来越多
- DSL 与具体的代码实现无关,它描述的是要做什么,而不关心该怎么做
- DSL 是一种表达与运行的分离方案
- 相同的模型具有不同的形态和表现形式,可以通过 DSL 支持不同的表达方式
- 例如,
<and><eq name="status" value="1" /><gt name="amount" value="3" /></and>
展现在前台,便对应于一个查询表单,应用到后台,则对应于Predicate
接口的实现, 发送到数据库中,则转化为 SQL 过滤条件的一部分。 而这一切,并不需要人工编码,它们只是同一信息的多重诠释而已
- 例如,
- 支持差量合并,以实现 DSL 复用和扩展
- 通过前缀引导实现 DSL 的分层设计,
支持多种风格的 DSL,可同时在 XML DSL 中引入代码(代码也是一种 DSL)。
不同的 DSL 由不同的解释器解析后再合并到上层 DSL AST 树中,
当前层的 DSL 解释器不需要理解和关注其他层的 DSL 结构和符号。
除了父子节点的 DSL 形式,还可以通过 XML Namespace 在 DSL 节点上标注
其他层的属性(比如,
<prop name="abc" graphql:labelProp="abc_label"/>
), 从而在当前 DSL 层附加上其他层的额外信息,可被GraphQL
层直接识别和处理 - 一个 DSL 包含
引入
(xmlns:xx
)、扩展
(extends
)、结构
(schema
)三个外部源。 引入可以通过名字空间使用其他 DSL 层的属性和标签,从而支持同一份 DSL 可进行多种形式的表达, 扩展就是在其他 DSL (可以是不同层)的基础上进行差量调整以达到当前 DSL 的需求, 而结构则是用于限定当前 DSL 结构的(限定可用节点名称、属性类型等) - 不同的编程语言的实现就是一个个不同的坐标系统,相同的 DSL 可以在不同的坐标系统中进行表达, 也就是,DSL 是语言无关的,可以有多种实现方式
XLang
- XDef 是 XLang 中的领域模型定义语法,用于定义模型结构,约束属性数据,
作用类似于采用 XML 语法的 XSD 语言和采用 JSON 语法的 JSON Schema。
XDef 与它们的区别在于,它强调两点:
- schema 描述与领域描述本身是同形的,也就是说schema 的 Tree 结构与领域模型的 Tree 结构是一致的
- 所有的集合元素都通过
xdef:key-attr
定义有唯一属性,从而确保领域描述的每个节点都有稳定且唯一的 xpath 路径
- 对于每一个集合元素,XDef 需要明确定义用于区分不同子元素的唯一属性,借助这一属性, 可以高效、稳定的实现领域结构的 diff 算法(类似于 vue 框架中虚拟 DOM 的 diff), 因此使用 XDef 定义一个领域结构,同时也意味着定义了该领域结构的差量化结构,我们可以很容易的计算两个领域结构的差与和
- 内置
x:extends
和x:gen-extends
语法结构,所有 DSL 自动具有差量编程和元编程的能力, 可以自由调用所有 Xpl 标签库,无需额外设计继承、库等抽象机制 - XLang 中不同的 DSL 共享了同样的扩展机制,使得它们只需要关注最核心的领域模型结构即可,
而
x:gen-extends
的全部作用可以被看作是编译期的一种代码生成技术(产生式编程), 简单的说就是不断拼接、转换、输出字符串或者 XNode,无论内部执行过程多复杂, 引入多少 XPL 标签库,最终实际送到 DSL 解析器里的只有符合 XDef 定义的 DSL 模型文本
结构化
- 核心为树结构,开发应用的过程,就是调整树结构的过程
- 一个应用就是一颗树,其中的模型就是子树
- 将数据结构化,可以避免在对象跨边界交换时出现信息丢失,使得对信息的自动化处理成为可能
- 可逆计算强调逻辑结构的可逆转化,也就是在系统 A 中需要保留一些元信息(metadata) A',
以便于可以从系统 A 直接转换为系统 B,也需要在系统 B 中保留元信息 B',
从而实现从系统 B 转换为系统 A。
(data, metadata)
配对才是信息的完整表达, 这和消息对象总是包含(body, headers)
是一个道理