ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

SE-0041 协议命名约定提案复盘:从 `Creatable`/`Convertible`/`Representable` 到 Swift 字面量协议的演进之路

SE-0041 协议命名约定提案复盘:从 `Creatable`/`Convertible`/`Representable` 到 Swift 字面量协议的演进之路 文档【免费下载链接】swift-evolutionThis maintains proposals for changes and user-visible enhancements to the Swift Programming Language.项目地址https://gitcode.com/gh_mirrors/sw/swift-evolution点击查看免费下载本文以 SE-0041 提案全文 为核心骨架结合仓库内后续提案SE-0115、SE-0089、SE-0213 等梳理 Swift 标准库中转换类协议命名约定从混乱走向清晰的完整脉络说明该提案为何被否决、其核心思想又如何以另一种形态沉淀进 Swift 3.0 之后的语言与标准库中。导读SE-0041《Updating Protocol Naming Conventions for Conversions》是 Swift 演进历史上一份未通过却影响深远的提案它针对标准库中Convertible后缀语义分裂的问题提出了Creatable、Convertible、Representable三套命名约定最终虽被评审委员会否决却直接催生了 SE-0115 对全部字面量协议的ExpressibleBy*Literal重命名。阅读本文你将理解 Swift 协议命名约定的演进动机、SE-0041 的完整设计与被否决原因以及它留下的思想遗产如何在后续提案中落地生根。背景Convertible后缀的语义分裂Swift 标准库包含八十余个协议其中约 15% 与类型初始化、类型转换相关见 SE-0041 引言。标准库为协议名建立了后缀约定但Convertible一词被用在了两种完全不同的方向上多数Convertible协议表示从协议名所含类型转换而来from例如IntegerLiteralConvertibleCustomStringConvertible与CustomDebugStringConvertible两个协议表示到协议名所含类型的转换to而RawRepresentable在原始值与类型之间做双向转换却完全没有使用 convert 这个词。三个语义截然不同的方向共用一个或不同后缀给开发者自行命名协议时带来了极大的困惑——这正是 SE-0041 的出发点为从类型转换而来转换到某类型双向转换三类任务分别建立清晰、一致、有意义的后缀约定。值得注意的历史先例Swift 团队过去多次通过改名让协议名更清晰例如LogicValue改为BooleanType、Printable/DebugPrintable改为CustomStringConvertible/CustomDebugStringConvertible、ExtensibleCollectionType并入RangeReplaceableCollectionType。SE-0041 自认是这一改名传统在转换类协议上的延续。核心设计三套命名后缀约定SE-0041 提出为三类转换语义分别规定协议后缀且刻意不约束实现细节——不规定必须用初始化器还是静态工厂方法创建实例也不规定成员如何命名Creatable从……创建表示从协议名中提到的类型或关联类型转换而来如IntegerLiteralConvertible、StringLiteralConvertible等。标准库中此类协议数量最多。Convertible可双向转换表示既能投影到协议名中的类型也能从该类型转换回来。标准库中唯一代表是RawRepresentable。Representable可表示/投影为……表示投影到协议名中提到的类型即把CustomStringConvertible、CustomDebugStringConvertible归入此类。按照该约定受影响的标准库协议清单如下新约定现役协议按提案原文CreatableArrayLiteralConvertible、BooleanLiteralConvertible、DictionaryLiteralConvertible、ExtendedGraphemeClusterLiteralConvertible、FloatLiteralConvertible、IntegerLiteralConvertible、NilLiteralConvertible、StringInterpolationConvertibleConvertibleRawRepresentableRepresentableCustomStringConvertible、CustomDebugStringConvertible对既有代码的影响与迁移路径提案对迁移影响做了分级评估见 Impact on existing code直接引用被改名协议名的代码将无法编译需要同步更新命名或依赖编译器提供弃用警告deprecation warning与 fixit 自动修复仅使用这些协议成员如description的代码不受任何影响因为协议成员本身未变用户自定义的Convertible/Representable/Creatable后缀协议仍可继续编译运行但会与新 API 指南脱节。配套迁移手段包括Xcode 检测遵循旧约定命名协议并给出重命名 fixit、对从事转换却不符合任何约定的协议发出警告或由用户手动按新约定改名。提案强调这种机械式改名有充分的先例如Printable→CustomStringConvertible。评审交锋标准库设计团队的三大异议与作者的回应提案记录了与标准库设计团队Standard Library design team评审意见的正面交锋见 Response to Design Team Feedback这是理解该提案命运的关键异议一方案本身不能澄清方向性设计团队认为把Representable与Convertible互换并不能澄清方向性XXXConvertible的直觉含义恰恰是可转换为 XXX。作者的回应从词源语义出发辩护Convertible暗示可交换/双向关系——敞篷车convertible car可在敞篷与硬顶之间切换沙发床convertible sofa既当床又当沙发convert 意味着 A R b 与 b R a 同时成立Represent意味着以某种特性或品质取而代之/代表——律师可以代表我但我不能反过来代表我的律师即 A R b 与 b R a 不对称Create表示创造出新事物——与 initializing 语义有距离但实践中重叠例如IntegerLiteralConvertible的实际含义是符合该协议的类型可以用整数字面量建立自身实例。异议二LiteralConvertible 的命名应另辟蹊径设计团队提出将LiteralConvertible协议下沉到Swift.Syntax子命名空间并去掉 Convertible 后缀StringInterpolationConvertible改为InterpolatedStringLiteral或以空enum Syntax内嵌 typealias 的方式组织如extension ArraySlice : Syntax.ArrayLiteral { }。作者回应称这是超出提案范围的实现细节SE-0041 只关心协议名的核心语义约定。不过这一Syntax 命名空间思路后来确实在 SE-0115 的早期草案中被尝试过见下文。异议三Literal 之外的真实样本不足设计团队认为字面量类之外只有 3 个真实例子其中仅 2 个相似不足以据此敲定全局约定。作者回应语义覆盖转换到某类型 / 从某类型转换 / 双向转换三类且自家代码与 GitHub 第三方代码中转换任务足够常见标准化命名仍有价值。修订版方案从三分类收敛为两分类吸收设计团队反馈后提案在 Updated Approach 中放弃三分类收敛为两个最核心的约定Initializable表示从协议名中的类型或关联类型转换而来取代原Creatable。该约定涵盖初始化器、工厂方法以及任何导入一个既有值以建立新实例的方式。提案举例遵循ArrayLiteralInitializable后既可用Set(arrayLiteral: someArray)又可用var set: SetT []创建集合。这一约定也回应了分离初始化器与工厂方法的呼声——提案认为按创建方式initializer vs factory再细分过于面向实现故统一并入Initializable。Representable表示主要目的是投影到协议名中的类型。原Convertible与Representable两类在此合并CustomStringConvertible、CustomDebugStringConvertible、RawRepresentable想象中会变为CustomStringRepresentable、CustomDebugStringRepresentable与维持现名RawRepresentable。Representable不承诺双向转换——某些Representable协议可以额外提供从表示类型反向初始化的要求但那不属于命名契约的一部分。未来方向修订版不再为双向转换单独设类。Swift 中这种契约本就罕见标准库里除RawRepresentable外几乎没有无损双向转换的例子而RawRepresentable归入Representable更贴切。若未来确有需要提案保留Isomorphic后缀备用。被否决之后命名思想如何落地为 Swift 现实SE-0041 最终状态为Rejected。但它的余波直接塑造了今天 Swift 的协议命名体系仓库中的后续提案完整记录了这条演进链1. SE-0115*LiteralConvertible→ExpressibleBy*LiteralSwift 3.0 实现SE-0115《Rename Literal Syntax Protocols》见 proposals/0115-literal-syntax-protocols.md明确指出自己是 SE-0041 的后续提案其动机部分原样继承了Convertible一词双向分裂的诊断但采取了不同解法标准库团队认为字面量协议与转换无关它们是在采纳语言提供的某种语法名称里的 Convertible 是红鲱鱼——类型系统里根本不存在 IntegerLiteral 这个实体字面量直接被类型化为对应字面量类型如Int、String调用点处用户看不到任何可见转换。于是 10 个协议被整体改名协议成员要求完全不变旧名SE-0041 列出的Creatable清单新名NilLiteralConvertibleExpressibleByNilLiteralBooleanLiteralConvertibleExpressibleByBooleanLiteralFloatLiteralConvertibleExpressibleByFloatLiteralIntegerLiteralConvertibleExpressibleByIntegerLiteralUnicodeScalarLiteralConvertibleExpressibleByUnicodeScalarLiteralExtendedGraphemeClusterLiteralConvertibleExpressibleByExtendedGraphemeClusterLiteralStringLiteralConvertibleExpressibleByStringLiteralStringInterpolationConvertibleExpressibleByStringInterpolationArrayLiteralConvertibleExpressibleByArrayLiteralDictionaryLiteralConvertibleExpressibleByDictionaryLiteral改名后的协议此后被标准库与后续提案广泛使用例如Numeric继承ExpressibleByIntegerLiteral见 proposals/0104-improved-integers.md、dynamicMemberLookup依赖ExpressibleByStringLiteral见 proposals/0195-dynamic-member-lookup.md、dynamicCallable的参数类型依赖ExpressibleByArrayLiteral/ExpressibleByDictionaryLiteral见 proposals/0216-dynamic-callable.md。SE-0115 还记录了两处重要的语义澄清Dave Abrahams 的经典反驳遵循IntegerLiteralConvertible并不意味着能用T(integerLiteral: 43)或T(43)初始化——func fT: IntegerLiteralConvertible() - T { return 43 }合法但前两种写法都是编译错误。协议的真实含义是该类型的实例可以写成一个字面量用字面量初始化是普遍的误解。Nate Cook 指出字面量协议命名容易加剧字面量与类型的混淆并给出x[1..2] [10, 20]可行而x[1..2] y不可行的经典例子说明类型推断在字面量处的黑魔法。SE-0115 的早期草案正是 SE-0041 评审中设计团队建议的Syntax空枚举命名空间方案Syntax.NilLiteral等因使用点读起来像类型即字面量而被放弃最终采纳 Sean Heber 提议的ExpressibleBy*Literal命名。2. SE-0089LosslessStringConvertible细化CustomStringConvertible家族SE-0041 修订版把CustomStringConvertible归入 投影到 String 的Representable语义SE-0089见 proposals/0089-rename-string-reflection-init.md则在这个家族中进一步分出无损层级新增LosslessStringConvertible : CustomStringConvertible要求init?(_ description: String)可从字符串无损还原实例如整数1050↔ 字符串1050并让字符串插值优先走该协议以规避昂贵的反射路径。这印证了 SE-0041 中转换类协议值得体系化命名的判断。3. SE-0213字面量协议与T(literal)初始化语义Swift 5.0 实现ExpressibleBy*Literal家族在 Swift 5.0 迎来语义增强SE-0213《Literal initialization via coercion》见 proposals/0213-literal-init-via-coercion.md规定对形如A(B)且B为字面量的调用若A遵循对应字面量协议则直接按B as A构造绕过普通初始化器查找。UInt64(0xffff_ffff_ffff_ffff)这类此前会编译期溢出的表达式因此变得合法Character(ab)也从运行期错误变成编译期错误。字面量协议在现代 Swift 中的核心地位可见一斑。4.RawRepresentable与Convertible语义的最终归宿SE-0041 中关于RawRepresentable双向转换语义的讨论也有后续SE-0033见 proposals/0033-import-objc-constants.md利用RawRepresentable将 Objective-C 常量导入为类型安全的枚举/结构体其示例struct NSErrorDomain : RawRepresentable展示了原始值类型转换的典型形态SE-0086见 proposals/0086-drop-foundation-ns.md将NSStringEncoding重构为String.Encoding : RawRepresentable结构体SE-0088 中OptionSet的ProcessEvent、CloseFlags等大量类型同样通过RawRepresentable与整型原始值对接见 proposals/0088-libdispatch-for-swift3.md。另一方面SE-0055见 proposals/0055-optional-unsafe-pointers.md讨论过从指针类型移除NilLiteralConvertible一致性——这一从字面量转换的协议恰好是 SE-0041Creatable清单的首项后来随 SE-0115 变为ExpressibleByNilLiteral。而Convertible后缀本身也未被浪费SE-0069 中ReferenceConvertible : _ObjectiveCBridgeable, CustomStringConvertible, ...见 proposals/0069-swift-mutability-for-foundation.md、SE-0089 的LosslessStringConvertible都延续了可转换为某类型的直觉与 SE-0041 作者为Convertible辩护的双向性语义形成了有趣的对照——标准库最终选择了设计团队那一侧的理解XXXConvertible 的含义就是可转换为 XXX。备选方案全览从 Instantiable 到 Isomorphic提案 Alternatives considered 记录了命名讨论的全谱系原始草案三分类Convertiblefrom、Representable双向、Projectableto——改动最小但方向语义不清晰从……创建候选词Instantiable、Initializable、Establishable、Constructable、Creatable可投影为……候选词Representable、Expressible、Presentable、Projectable可表示且可实例化候选词Convertible、Representable为工厂方法单独命名Building、Producer/Producing、Establishable等曾被考虑最终因过于面向实现被否决——这个决定直接影响了修订版Initializable的表述统一涵盖初始化器与工厂方法为未来双向转换保留的后缀Isomorphic。历史回望SE-0041 的遗产从今天的视角回看SE-0041 留下了三笔遗产问题诊断被完整继承——Convertible后缀语义分裂这一诊断在 SE-0115 中被原样引用并成为其动机基石命名空间的探索成为铺垫——设计团队建议的Syntax命名空间在 SE-0115 早期草案中试验后放弃最终演化出ExpressibleBy*Literal这一更贴合调用点直觉的命名投影to/创建from的方向性框架——虽然具体后缀名被否决但其转换方向应反映在协议名中的核心思想通过ExpressibleBy*Literalfrom 字面量、CustomStringConvertible/LosslessStringConvertibleto String、RawRepresentableraw value 互转等今日标准库协议延续至今。对希望为自己的库设计转换类协议的开发者而言SE-0041 的完整评审记录本身就是一份命名方法论教材命名要避免一词多义、方向性要体现在协议名中、语义约定不应绑定实现细节而社区评审中的词源学辩论convert/represent/create 的方向性至今仍有参考价值。深入阅读SE-0041 提案原文SE-0115 字面量语法协议重命名Swift 3.0 实现SE-0089String.initT(_: T)重命名与LosslessStringConvertibleSwift 3.0 实现SE-0213 字面量初始化经由强制转换Swift 5.0 实现SE-0055 指针类型移除NilLiteralConvertible的讨论SE-0033RawRepresentable导入 Objective-C 常量SE-0086String.Encoding : RawRepresentable结构体重构SE-0104Numeric : ExpressibleByIntegerLiteral赞分享文档【免费下载链接】swift-evolutionThis maintains proposals for changes and user-visible enhancements to the Swift Programming Language.项目地址https://gitcode.com/gh_mirrors/sw/swift-evolution点击查看免费下载相关推荐A2A 协议版本演进全解析从 0.2.1 到 1.0.1 的协议成熟之路A2A 协议版本演进全解析从 0.2.1 到 1.0.1 的协议成熟之路 Agent2AgentA2A协议是 Linux Foundation 旗下、由人工智能AI AgentAPI设计Eclipse Mosquitto MQTT协议实现从规范到标准的演进Eclipse Mosquitto MQTT协议实现从规范到标准的演进 MQTTMessage Queuing Telemetry Transport消息物联网消息队列后端网络/通信MusE插件开发指南如何为MusE创建自定义LV2和DSSI音频插件MusE插件开发指南如何为MusE创建自定义LV2和DSSI音频插件 MusE是一款功能强大的数字音频工作站支持音频和MIDI处理。本指南将帮助你快速掌握为音视频上一篇DBX安全特性全解析离线使用、无遥测和连接安全的最佳实践下一篇Minecraft编程革命用Python自动化你的方块世界 创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进