Skip to content

1-layout/struct 提案1:/lib 注册的具名结构体(定义 kind=struct,实例 kindexpr=/{lib}/{struct},成员访问共享 ·) #199

Description

@miaobyte

背景与动机

kvlang 目前没有具名结构体。要表达"一条有固定字段、字段类型各异的记录",只能用无类型 {}object)+ · 成员访问手搓(见 tutorial/12-struct/*:链表/树/图全是 l·head·val = 10 这种裸 dict)。缺点:

  • 字段名写错、类型给错,无从检查,错误静默。
  • 没有"类型"这个一等实体:无法声明 p: Point、无法在 rwir 签名里约束参数是某结构。
  • #67(否决递归 JSON 文法)、no-json-kind(JSON 全类型靠 object/stringkeymap + 动态元素覆盖)留下的缺口对应——异构具名记录这一档一直空缺。

关键观察:struct 与 stringkeymap 在物理存储上完全同构,差别只在 schema。因此 struct 不需要新的存储机制,只需让实例 kindexpr 指向 /lib 下的类型定义节点,并复用 stringkeymap 的 · 成员访问

符号职责:/ 指向类型,· 访问成员

  • / = tree index:类型定义就是 KV 树里的一个节点,实例的 kindexpr 直接写这个节点的完整路径 /{lib}/{structname}(如 /lib/Point)。引用一个类型 = 指向它在树里的位置,天然用 /
  • · = member index:只用于访问实例成员 p·x(与 stringkeymap m·key 同符号)。

两者不混用:类型引用永不出现 ·,成员访问永不出现 /

核心设计:struct = 注册在 /lib 的具名类型(kind=struct),实例 kindexpr = /{lib}/{structname}

沿用容器方案2(kvlang-container-memindex-split):任何容器落盘恒三层

  • p —— 值 XValue(kind 标记 + dims 落 head,body 空)
  • —— memindex(kind=index,body=[4B count][name\n…],成员列表唯一权威)
  • p·k —— 成员值(独立 key)

struct 实例完全套用这套,唯一变化是 p 的 kind 不是 stringkeymap/object,而是类型定义节点的完整路径 /{lib}/{structname}

1. 定义(definition)

结构定义注册进 lib 命名空间(与 rwir/rwfunc 同级,/ 分隔):

struct Point {
    x:float64=0.0
    y:float64=0.0
}

字段语法 name:kindexprname:kindexpr=default(冒号分隔类型,可选 = 给默认值)。kindexpr 即签名类型表达式(float64[]float32[]char/utf8·int64、乃至另一个 struct 路径 /lib/Node)。layout 产物(默认包,pkg 规则与 rwfunc 一致):

/lib/Point            kind=struct        # 类型节点:其 kindexpr 就是关键字 struct(一档新 kind)
/lib/Point·           kind=index         # 字段列表 memindex: "x\ny"
/lib/Point·x          kind=float64 =0.0  # 默认值 XValue:头部 kind 即字段 kindexpr,body 即默认值
/lib/Point·y          kind=float64 =0.0

关键:字段描述符 /lib/Point·x 存的就是默认值 XValue —— XValue head 本就携带 kindexpr(kind+dims),body 就是默认值,一举给出"字段类型 + 字段默认值"两件事,无需另存类型字符串。省略 =default 时落该 kindexpr 的零值。

于是 /lib/Point· 整棵子树 = 一个默认值原型实例(一个 x=0,y=0 的 Point)。/lib/Point 这个 XValue 的 kind 就是 struct(不是 []char/utf8·[]char/utf8);structrwir/rwfunc/scope/stringkeymap 并列,进 known_kind

2. 实例 kindexpr

实例的 kind(XValue head 里的 kindexpr)= 类型定义节点的完整路径 /lib/Point/ 分隔,无 ·)。

  • 解析(runtime,非 layout):kindexpr /lib/Point 就是定义路径,直接据它读 · memindex 与各字段 kindexpr,无需任何变换。
  • 成员访问另用 ·p·x),与类型引用的 / 井水不犯河水。

3. 创建(creation)

具名字面量(对齐 Go/Rust):

p = Point{x = 1.0, y = 2.0}
p2 = Point{x = 1.0}          # y 省略 → 取定义里的默认值 0.0
p3 = Point{}                 # 全省 → 全默认值(= 克隆原型)

创建语义 = 克隆原型 + 覆盖给定字段:runtime 拷贝 /lib/Point· 子树(默认值原型)到实例根,再用字面量给出的字段覆盖。实例值标记 kind 记为 /lib/Point

p            kind=/lib/Point   # 实例值标记(kind = 类型节点完整路径)
p·           kind=index        # "x\ny"
p·x          float64 1.0
p·y          float64 2.0

字段存在性、字段类型是否匹配定义、省略字段取默认 —— 由 runtime 完成,layout 不读 /lib(见"检查职责")。

4. 读写整体(whole read/write)

  • q = p —— 深拷贝子树(pp·*)到 q,值语义(对齐 C/Go/Rust 的 struct 值拷贝)。
  • 作 rwir 读参:按根路径 p 传入,读参读整棵子树;作写参:写整棵子树。
  • kv·get/set/deltree 作用于 p 走既有容器子树语义,struct 不新增原语。

5. 读写成员(member read/write)—— 与 stringkeymap 共享 ·

  • 读:p·x → 单跳 kv get key p·x。静态成员链在 layout 编译期拼完整路径(#110),a·b·c → key a·b·c
  • 写:p·x = 3.0kv·set(p·x, 3.0)
  • 与 stringkeymap m·"key" 字面同形(同一个 ·);差别仅在字段是编译期名 vs 运行期值。

6. 回收(reclaim/free)

  • kv·deltree(p):memindex 是权威成员表,遍历它删除每个 p·field;字段自身是容器/struct/extindex 则递归;extindex 字段(落扩展存储的大值)经其扩展句柄释放。与 object/stringkeymap 完全同一套 GC,无新机制。
  • scope 内的局部 struct 随帧结束回收。

检查职责:layout 不检查 /lib

layout 不读、不校验 kvspace 的 /lib(与 extension-rwir-not-hardcoded 一致:判定全留给 runtime)。因此:

  • layout 只负责:解析 struct Point{…} → lower 为 /lib/Point 注册;解析 Point{…} 字面量、p·x 成员访问 → lower 为路径与容器落盘。
  • 字段存在性、字段类型匹配 —— runtime 拿 kindexpr 路径 /lib/Point,读其 · memindex 与各字段 kindexpr 做判定并报错。
  • 类型是否已注册 —— 同样 runtime 判定(/lib/Point 不存在则 runtime 报错)。

参考五大语言

定义 创建 成员访问 回收
C struct Point{double x,y;} struct Point p={1,2}; p.x 栈自动 / free
Go type Point struct{X,Y float64} Point{X:1,Y:2} p.X GC
Rust struct Point{x:f64,y:f64} Point{x:1.0,y:2.0} p.x Drop / 所有权
Python @dataclass class Point: x:float=0;y:float=0 Point(1,2) p.x GC
TypeScript interface Point{x:number;y:number} {x:1,y:2} p.x GC
kvlang(本提案) struct Point{x:float64=0.0;y:float64=0.0}/lib/Point(kind=struct) Point{x=1.0}(余取默认) p·x deltree 驱动)

字段默认值:C(无,C++ 有)、Go(无,取零值)、Rust(无,靠 Default/struct-update)、Python dataclass 有 x:float=0.0、TS class 字段有。kvlang name:kindexpr=default 对齐 Python dataclass 的字段默认语义,且默认值直接以 XValue 落进定义子树。

成员访问符:C/Go/Rust/Py/TS 一律 .;kvlang 用 ·. 已留给小数,见 const.h MEMBER_SEP 注释)。struct 与 stringkeymap 在 kvlang 里统一到 ·,这正是本提案的落点。

参考 stringkeymap(核心对比)

维度 stringkeymap struct(本提案)
键集合 开放、运行期增长 闭合、定义期固定
值类型 同构(单一 value kindexpr) 异构(每字段独立 kindexpr)
kind stringkeymap struct(定义节点)/ /lib/Name(实例引用完整路径)
成员访问 m·key(key 是运行期值) s·field(field 是编译期名)
检查 layout 查 key/value 类型 runtime 查字段存在性+类型(layout 不查 /lib)
物理落盘 p + + p·k 完全相同

一句话:struct 就是"字段名闭合、字段类型异构、由 /lib 具名类型驱动"的 stringkeymap;物理层零新增,实例 kindexpr 只是指向类型节点的路径 /lib/Name,schema 校验留给 runtime。

kindexpr 文法扩展

现 atom = shape | mapexprlayout/src/kindexpr.rs)。新增:

atom      = shape | mapexpr | structref
structref = "/" path          # /lib/Point:指向 lib 下类型定义节点的完整路径

消歧(三者互斥):

  • structref 恒以 / 起头(路径)—— 现文法中 kindexpr 无 /,正好腾出。
  • mapexpr 恒含 · 且 key 段以 [ 起头(valid_key 已强制)。
  • shape 的 base 是 known_kindany

layout 侧 valid_kindexpr 只做语法承认(/ + 合法路径段),不解析 /lib 是否存在该类型 —— 存在性/字段一致性由 runtime 判。

待定问题(供讨论)

  1. 引用路径形态:绝对 /lib/Point(本提案取此);含包时 /lib/geom/Point 的 pkg 段规则;是否允许省略包段的相对引用。
  2. 嵌套 / 递归 struct:链表节点 next: /lib/Node 需指针语义——关联 ptr-full-kindexpr-single-hop(指针存完整 kindexpr+key,Set 期单跳检查)。struct 字段为 struct 时是内联子树还是指针?
  3. 值语义 vs 引用q = p 深拷贝子树;大 struct 是否退化为 extindex/指针以省拷贝。
  4. 方法:是否允许 Point·dist 绑定 rwfunc,与 stringkeymap/lib 命名空间统一。
  5. 匿名 / 内联 struct:无 /lib 注册的一次性结构是否允许(退化为当前 object)。
  6. #151(stringkeymap→scatterarray 改名)协同:改名后 struct 与 scatterarray 的对称叙述。

关联

  • kvlang-container-memindex-split(容器方案2,· 唯一 memindex 标记)
  • extension-rwir-not-hardcoded(layout 不硬编码 /lib,判定留 runtime)
  • #187 kindexpr · map key·value
  • #67(否决递归 JSON 文法)、no-json-kind(struct 是异构具名记录这一档的答案)
  • #151 scatterarray 改名
  • #110 静态成员链编译期拼完整路径
  • tutorial/12-struct/*(现用无类型 dict 手搓,本提案的目标替代物)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions