[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fsyLV5PSSulhS5KOk82F1ea9vPpOEj15Ji0YcCqtJAG0":3},{"item":4,"related":48},{"id":5,"type":6,"title":7,"slug":8,"summary":9,"body":10,"coverUrl":11,"productScreenshots":12,"productLinks":13,"authorName":14,"authorUrl":15,"authorSubject":16,"category":17,"tags":22,"sourceLabel":39,"sourceName":40,"sourceUrl":41,"status":42,"seoTitle":7,"seoDescription":9,"canonicalUrl":39,"isFeatured":43,"sno":44,"sortOrder":45,"publishedAt":46,"updatedAt":47,"createdAt":47},"38eda4eb-5c9e-402e-aec4-d72dc9c8184b","article","Refinement Types：类型不只说“这是整数”，还可以说“它大于 0”","refinement-types-liquid-haskell-explained","Refinement Types 在基础类型上附加可检查的条件，让“正数金额”“有限长度字符串”等业务不变量进入类型系统。本文解释它与运行时校验的关系，以及它如何帮助 AI 生成代码表达更精确的正确性。","普通类型系统会告诉我们一个值是整数、字符串或用户对象，但很多业务规则比这更细：订单金额必须大于 0，数组下标必须落在范围内，用户名必须符合某种格式。Refinement Type，中文常译为“精化类型”或“细化类型”，就是在基础类型上再附加一个谓词条件。\n\n## 从“整数”到“满足条件的整数”\n\n`Int` 只能表达“这是一个整数”。精化类型可以表达“这是一个大于 0 的整数”或“这是一个长度小于 256 的字符串”。如果函数要求接收前一种值，类型检查器或验证工具就有机会在调用点检查这个前提。\n\n这并不意味着所有业务规则都能自动证明。工具通常会把精化条件转成逻辑约束，再交给求解器。如果条件太复杂、涉及外部系统或缺乏足够的函数规格，开发者可能需要提供辅助证明或接受分析器无法确定。\n\n## 它和运行时校验是什么关系\n\n运行时校验是在程序执行时检查条件，例如收到订单后判断金额是否为正。精化类型则尝试把一部分检查提前到编译或验证阶段。两者不是互斥关系：外部输入仍然不可信，需要先验证；一旦验证完成，内部函数可以携带更精确的类型信息，减少重复检查。\n\nLiquidHaskell 是一个典型实践：开发者可以给已有 Haskell 函数补充精化规格，描述返回值与输入之间的关系。这个方式很适合讲清“类型不仅是分类标签，也可以是可检查的契约”。\n\n## AI 编程为什么需要它\n\nAI 可能生成一个函数，类型上完全正确，但业务上仍然把负数当价格、空列表当必有元素。精化类型把这些隐含要求显式写出来，帮助人类和工具沟通“正确到底是什么意思”。\n\n但不要把它当成万能护盾。精化规则越复杂，维护和验证成本越高；如果团队没有持续维护规格，类型系统也无法替大家理解业务。\n\n## 读者应该记住\n\n精化类型把“类型正确”推进了一步：不仅知道值属于哪一类，还能表达它必须满足什么条件。它适合把关键业务不变量写成机器可检查的契约。\n\n资料：[LiquidHaskell Refinement Types 101](https:\u002F\u002Fucsd-progsys.github.io\u002Fliquidhaskell\u002Fblogposts\u002F2013-01-01-refinement-types-101.lhs\u002F)；[LiquidHaskell Specifications](https:\u002F\u002Fucsd-progsys.github.io\u002Fliquidhaskell\u002Fspecifications\u002F)","\u002Fuploads\u002F2026-09-20\u002Fb89b7938-0a1d-4aee-b407-fae7fe2c4cab.jpg",[],[],"Foundit","https:\u002F\u002Ffoundit.cn","foundit-ai-editorial",{"id":18,"name":19,"slug":20,"description":21},"6179d3b6-dc34-4483-9ded-3cd9f1b37a47","科普","abbreviation","介绍各领域新兴概念",[23,27,31,35],{"id":24,"name":25,"slug":26},"144abe77-0dc6-4f66-a176-20bddb1c0bfa","编程","coding",{"id":28,"name":29,"slug":30},"a202d639-99a6-488a-a712-4d4c6ffd7e15","开发","dev",{"id":32,"name":33,"slug":34},"4c2bbea6-eab7-40a8-8447-1de478ff7749","分析","analyse",{"id":36,"name":37,"slug":38},"7c76bfc2-f80f-4ee0-a95d-27bd8708b434","技术","slug",null,"LiquidHaskell Refinement Types 101","https:\u002F\u002Fucsd-progsys.github.io\u002Fliquidhaskell\u002Fblogposts\u002F2013-01-01-refinement-types-101.lhs\u002F","published",false,49,0,"2026-09-20T00:00:00.000Z","2026-09-20T03:57:53.323Z",[49,57,65],{"id":50,"type":6,"title":51,"slug":52,"summary":53,"coverUrl":54,"authorName":14,"sno":55,"publishedAt":46,"createdAt":56},"d0700249-defa-43a1-a66a-4455c6889072","ABI：为什么源码能编译，二进制却不能互相调用？","application-binary-interface-abi-explained","ABI 是二进制世界的调用合同，规定参数传递、对象布局、符号命名和异常处理。本文区分 ABI 与 API，解释动态库、C++ 兼容性和跨语言绑定为什么不能只看函数签名。","\u002Fuploads\u002F2026-09-20\u002F33666a48-4e8f-42a3-9ffd-f4c490498689.jpg",42,"2026-09-20T03:58:01.058Z",{"id":58,"type":6,"title":59,"slug":60,"summary":61,"coverUrl":62,"authorName":14,"sno":63,"publishedAt":46,"createdAt":64},"c9f4e936-9533-4b9c-a1de-f03ef09fed37","WAL：为什么数据库要先写日志，再写真正数据？","write-ahead-logging-wal-database-explained","WAL 要求描述数据变化的日志先于数据页持久化，让数据库可以延迟刷写并在崩溃后通过重放恢复。本文用账本和收据解释 REDO、检查点、复制与持久性设置的关系。","\u002Fuploads\u002F2026-09-20\u002F6260c953-345b-4c9d-b3d6-6202c7e5539c.jpg",43,"2026-09-20T03:58:15.082Z",{"id":66,"type":6,"title":67,"slug":68,"summary":69,"coverUrl":70,"authorName":14,"sno":71,"publishedAt":46,"createdAt":72},"41d50774-50df-4e2f-b1b3-ab6d9f329726","Backpressure：生产者太快时，系统怎样不被数据淹没？","backpressure-reactive-streams-explained","Backpressure 让下游处理能力反过来影响上游生产速度，避免异步流水线靠无限缓存硬撑。本文用水管和阀门解释响应式流、需求信号、数据丢弃与容量设计，也说明它和普通限流的区别。","\u002Fuploads\u002F2026-09-20\u002F35c47792-3d4b-46ae-9375-c68e4c53330e.jpg",45,"2026-09-20T03:58:08.835Z"]