Any 设计

本文档收敛 Dujie 中 any 的语言定位、使用边界和显式收窄规则。

定位

any 是边界类型,不是万能类型,也不是动态对象系统。

它的职责是承接编译期无法完全静态确定的动态值,主要用于语言与宿主环境的边界交互。

当前最重要的使用场景:

  • main(props: map<string, any>) -> widget
  • element(..., props: map<string, any>, ...)
  • 少量调试、日志和宿主互操作场景

基本原则

any 只用于动态边界

T -> any 允许

any -> T 不做隐式恢复

any 不参与普通静态运算

any 不作为默认推导结果

允许的方向

装箱

普通值可以进入 any

  • 基础类型
  • opt<T>
  • list<T>
  • map<K, V>
  • struct
  • widget

这里的“进入”只是语言层意义上的动态边界承载,不代表鼓励在普通静态代码里广泛使用 any

传递

any 可以:

  • 赋值给 any
  • 作为 any 参数传递
  • 作为函数返回值返回
  • 放进 map<string, any>

禁止的方向

any 不允许直接参与这些操作:

  • 算术运算
  • 比较运算
  • 逻辑运算
  • 成员访问
  • 下标访问
  • 直接作为 if 条件
  • 普通函数调用

也就是说,any 不会像动态语言那样自动“猜成某种具体值”后继续运算。

显式收窄

第一阶段,any 的显式恢复机制统一使用 is

支持的最小语法:

x is T
x is T(v)

语义:

  • x is T:判断运行时值是否属于 T,结果为 bool
  • x is T(v):判断是否属于 T;若成立,把具体值绑定到 v

作用域规则:

  • v 只在判断为真的分支中可见
  • v 不进入 else
  • v 不泄漏到 if 外部

例如:

let title = props["title"];

if title is string(s) {
    text(s)
} else {
    panic("title must be string")
}

当前稳定支持的收窄目标

第一阶段稳定支持的目标类型仅限基础类型:

  • int
  • float
  • bool
  • rune
  • string

list<T>map<K, V>opt<T>structwidgetis 判型,后续如需开放,另行设计。

main 的关系

当用户把 main 写成动态入口形式时,main(props: map<string, any>) -> widget 是最主要的 any 使用边界之一。

规则:

  • props["k"] 的结果是 any
  • key 不存在时直接 panic
  • 取出值后,再通过 is 做显式收窄

例如:

func main(props: map<string, any>) -> widget {
    let title = props["title"];

    if title is string(s) {
        text(s)
    } else {
        panic("title must be string")
    }
}

element 的关系

element.props 第一阶段保持:

map<string, any>

原因:

  • element 是宿主边界
  • 宿主属性集合天然开放
  • 第一阶段不引入联合类型

这里使用 any,不代表普通组件参数和普通静态数据模型也应使用 any

与类型推导的关系

any 不作为“推导失败后的兜底类型”。

也就是说:

  • let x = none 不会因为推不出类型就变成 any
  • []{} 也不会因为缺上下文就落成 list<any>map<any, any>

只有在以下情况下才进入 any

  • 用户显式写出 any
  • 边界 API 明确要求 any

与插值字符串的关系

插值字符串允许任意类型。

any 而言,插值采用展示语义:

  • 如果运行时能拿到更具体的承载值,则展示该值的展示结果
  • 如果拿不到更具体信息,则可以回退成类似 <any> 的摘要文本

这只是展示行为,不代表 any 在普通表达式中获得了隐式恢复能力。

map 键的关系

any 不允许作为 map 的 key 类型。

原因:

  • key 需要稳定的键语义
  • any 会把这层语义打散

当前不提供的能力

第一阶段不提供:

  • typeof(...)
  • match
  • 基于 any 的模式匹配系统
  • any 的隐式字段访问
  • any 的隐式函数调用
  • any 的隐式转换到基础类型

当前结论

any 的核心定位可以概括为一句话:

any 是用于动态边界和互操作的显式擦除类型,只允许装箱、传递和显式收窄,不允许隐式恢复为具体类型。`