fp-ts 中的 Traced 余单子(Comonad):基于 `P => A` 函数的环境读取与增量构建
开发工具【免费下载链接】fp-tsFunctional programming in TypeScript项目地址https://gitcode.com/gh_mirrors/fp/fp-ts点击查看免费下载导读Traced是 fp-ts 中一种以纯函数读取环境为形态的类型构造器TracedP, A本质上就是(p: P) A它把从某个可叠加的上下文P中读出值A封装成可组合的一等公民。本文以 Traced.ts 模块文档 为核心结合 Traced 源码 与 Traced 测试用例 中的ProjectBuilder实例完整讲解Traced的模型、Functor/Comonad实例以及censor、listen、listens、tracks四个工具函数的实现原理与实战用法。读完本文你将能够用Traced实现增量式、可组合的构建器如逐层叠加配置的工程脚手架生成器并理解余单子extract/extend在其中的作用。一、Traced是什么把读取环境变成类型1.1 模型定义在 fp-ts 中Traced的模型极其简单src/Traced.ts#L17-L19 中只有一行export interface TracedP, A { (p: P): A }也就是说一个TracedP, A就是一个接受P返回A的普通函数。类型参数PPosition位置或环境类型是读取的输入A读取到的值类型。从源码结构看Traced属于二元类型构造器两个类型参数因此在 src/Traced.ts#L104-L108 中通过URItoKind2注册进 fp-ts 的高阶类型HKT体系declare module ./HKT { interface URItoKind2E, A { readonly [URI]: TracedE, A } }配合 src/HKT.ts 中的URItoKind2Traced才能参与Functor2、Comonad2C等带 kind 的泛型实例构造。1.2 为什么要用函数做数据类型Traced的妙处在于函数本身就是一种数据结构。一个TracedP, A尚未被消费时它只是一个闭包只有当调用它并传入具体的P时值才会被计算出来。这带来两个直接优势延迟求值构建复杂的Traced链并不会立即执行任何计算可组合的上下文叠加借助MonoidP多个Traced的环境可以像覆盖配置一样逐层合并。模块文档在 docs/modules/Traced.ts.md 中将Traced归于model分类并标注 Added in v2.0.0说明它是 fp-ts 2.x 的基石抽象之一。二、实例Functor与getComonad2.1Functor对读取结果做映射模块文档中的Functor实例签名export declare const Functor: Functor2Traced对应的实现位于 src/Traced.ts#L114-L117export const Functor: Functor2URI { URI, map: _map }map的行为非常直观——先读取再变换src/Traced.ts#L90export const map: A, B(f: (a: A) B) E(fa: TracedE, A) TracedE, B (f) (fa) (p) f(fa(p))即map(f)(wa) (p) f(wa(p))它不改变环境P只对读取到的A做变换。测试 test/Traced.ts#L55-L58 验证了这一点const wa buildProject(myproject) U.deepStrictEqual(pipe(wa, _.map(getProjectName))(M.empty), myproject)Functor实例需要满足两条定律见 src/Functor.ts#L7-L10同一律map(fa, a a) - fa复合律map(fa, bc ∘ ab) - map(map(fa, ab), bc)由于Traced的map本质是函数复合这两条定律天然成立。2.2getComonadTraced的余单子结构模块文档的核心签名export declare function getComonadP(monoid: MonoidP): Comonad2CURI, P它依赖一个MonoidP才能为TracedP, _构造Comonad2C实例。实现见 src/Traced.ts#L62-L78export function getComonadP(monoid: MonoidP): Comonad2CURI, P { function extendA, B(wa: TracedP, A, f: (wa: TracedP, A) B): TracedP, B { return (p1) f((p2) wa(monoid.concat(p1, p2))) } function extractA(wa: TracedP, A): A { return wa(monoid.empty) } return { URI, _E: undefined as any, map: _map, extend, extract } }这里有两个关键操作extract把TracedP, A退化为A。做法是以monoid.empty幺元作为默认位置调用函数即wa(monoid.empty)。这对应 src/Comonad.ts#L15-L17 中Comonad接口对extract的定义extract: A(wa: HKTW, A) A。extend给定wa: TracedP, A和f: (wa: TracedP, A) B返回一个新的TracedP, B。它的实现是在新位置p1上把f应用到以p1为基准、用monoid.concat(p1, p2)偏移出的新读取函数上。extend继承自 src/Extend.ts#L39-L41 的Extend2C接口并向上提供Functor2C的map。从源码结构看MonoidP的empty和concat是Traced余单子语义的基石empty定义了起始位置concat定义了从当前位置偏移到相邻位置的方式。2.3 类型变量说明getComonadP返回Comonad2CURI, P其中C表示部分应用CurriedP已被固定剩下的A作为自由类型参数。这在 src/Comonad.ts#L39-L41 中有完整定义export interface Comonad2CW extends URIS2, E extends Extend2CW, E { readonly extract: A(wa: Kind2W, E, A) A }三、mapping 一族map与flap3.1map映射模块文档对map的说明是标准的 Functor 语义mapcan be used to turn functions(a: A) Binto functions(fa: FA) FBwhose argument and return types use the type constructorFto represent some computational context.签名Added in v2.0.0export declare const map: A, B(f: (a: A) B) E(fa: TracedE, A) TracedE, B注意这里的E就是模型里的P位置。map是柯里化的可直接配合pipe使用例如 test/Traced.ts#L56-L57 中pipe(wa, _.map(getProjectName))。3.2flap翻转应用flapAdded in v2.10.0是一个函数值翻转工具export declare const flap: A(a: A) E, B(fab: TracedE, (a: A) B) TracedE, B其实现只是基于Functor实例的派生src/Traced.ts#L123export const flap /*#__PURE__*/ flap_(Functor)而flap_的通用实现见 src/Functor.ts#L153-L155return (a) (fab) F.map(fab, (f) f(a))即先准备好一个值a: A再拿到一个环境读取函数TracedE, A B最后得到TracedE, B。语义上等价于把a应用到环境中的函数上。四、utils位置Position操作四件套这四个工具函数是本模块最具实用价值的部分全部 Added in v2.0.0实现在 src/Traced.ts#L27-L56。4.1listen取出当前位置Get the current positionexport declare function listenP, A(wa: TracedP, A): TracedP, [A, P]实现export function listenP, A(wa: TracedP, A): TracedP, [A, P] { return (e) [wa(e), e] }它把读取结果与当前位置打包成元组[A, P]相当于审计不仅知道读到了什么还知道是从哪个位置读的。测试 test/Traced.ts#L93-L107 展示了C.extract(_.listen(buildProject(myproject)))的结果是[Project, Settings]二元组。4.2listens取出依赖位置的派生值Get a value which depends on the current positionexport declare function listensP, B(f: (p: P) B): A(wa: TracedP, A) TracedP, [A, B]实现export function listensP, B(f: (p: P) B): A(wa: TracedP, A) TracedP, [A, B] { return (wa) (e) [wa(e), f(e)] }与listen的区别在于它不直接返回原始位置P而是先用f: (p: P) B对位置做一次变换再与读取结果打包。例如 test/Traced.ts#L109-L127 中_.listens((settings) settings.settingsTravis)取出的辅助值就是当前是否开启 Travis这个布尔派生量。4.3censor改写当前位置Apply a function to the current positionexport declare function censorP(f: (p: P) P): A(wa: TracedP, A) TracedP, A实现export function censorP(f: (p: P) P): A(wa: TracedP, A) TracedP, A { return (wa) (e) wa(f(e)) }censor不改变结果类型A而是在读取前用f对位置做改写——这正是覆盖配置的原语。测试 test/Traced.ts#L129-L147 中_.censor((settings) ({ ...settings, settingsHasLibrary: !settings.settingsHasLibrary }))把settingsHasLibrary取反最终C.extract得到settingsHasLibrary: true。4.4tracks按相对位置提取值Extracts a value at a relative position which depends on the current value.export declare function tracksP, A(M: MonoidP, f: (a: A) P): (wa: TracedP, A) A实现src/Traced.ts#L27-L29源码注释标注了 TODO: curry in v3即 v3 将调整柯里化顺序export function tracksP, A(M: MonoidP, f: (a: A) P): (wa: TracedP, A) A { return (wa) wa(f(wa(M.empty))) }tracks是最有意思的一个它先在默认位置M.empty上读取wa得到a再用f(a)计算出相对偏移量最后在偏移后的位置wa(f(wa(M.empty)))上取值。因此它被称为追踪——根据当前值决定下一步去哪个相对位置读取。测试 test/Traced.ts#L77-L91 给出了经典用法travisB _.tracks(M, (project) ({ ...M.empty, settingsTravis: project.projectGitHub }))即如果项目启用了 GitHub就顺带开启 Travis。将其与gitHubB通过C.extend组合后projectTravis正确变为true。五、type lambdasURI与类型注册模块文档的 type lambdas 部分包含两个条目均 Added in v2.0.0export declare const URI: Traced export type URI typeof URI对应 src/Traced.ts#L96-L102。URI是一个字面量类型标签Traced配合 src/Traced.ts#L104-L108 的模块扩展将TracedE, A注册进URItoKind2从而让Functor2、Comonad2C等带 kind 的抽象可以实例化到Traced上。这是 fp-ts 中所有高阶类型HKT参与泛型编程的标准机制。六、zone of death已废弃的traced模块文档最后一部分是 zone of death废弃区export declare const traced: Functor2Traced注释明确写着 UseFunctorinstead.改用Functor实现在 src/Traced.ts#L136export const traced: Functor2URI Functor从 src/Traced.ts#L129-L135 可以看到它带有deprecated标记。也就是说traced只是Functor的别名为 v2.0.0 的早期用户保留兼容性新代码应直接使用Functor。这也解释了模块文档中Functor实例为何标注 Added in v2.7.0traced在 v2.0.0 引入、v2.7.0 被正式实例替代。七、实战用Traced构建可组合的增量构建器测试 test/Traced.ts 提供了一个非常直观的实战模型Adapted from Comonadic builders我们把它完整展开展示Traced的完整工作流。7.1 定义环境与单子import * as B from fp-ts/boolean import { Monoid, struct } from fp-ts/Monoid import * as _ from fp-ts/Traced interface Settings { readonly settingsHasLibrary: boolean readonly settingsGitHub: boolean readonly settingsTravis: boolean } const M: MonoidSettings struct({ settingsHasLibrary: B.MonoidAny, settingsGitHub: B.MonoidAny, settingsTravis: B.MonoidAny }) const C _.getComonad(M)这里用 src/Monoid.ts#L140-L151 的struct把三个布尔Monoid合成一个结构体MonoidSettings。B.MonoidAny的empty是false、concat是逻辑或见 src/boolean.ts 中MonoidAny相关定义因此M.empty就是全默认关闭的配置。7.2 定义构建器interface Project { readonly projectName: string readonly projectHasLibrary: boolean readonly projectGitHub: boolean readonly projectTravis: boolean } interface ProjectBuilder extends _.TracedSettings, Project {} const buildProject (projectName: string): ProjectBuilder (settings) ({ projectName, projectHasLibrary: settings.settingsHasLibrary, projectGitHub: settings.settingsGitHub, projectTravis: settings.settingsTravis })ProjectBuilder就是一个TracedSettings, Project给它一份Settings它返回一个Project。7.3 用extend叠加特性const hasLibraryB (wa: ProjectBuilder): Project { const p { ...M.empty, settingsHasLibrary: true } return wa(p) } const gitHubB (wa: ProjectBuilder): Project { const p { ...M.empty, settingsGitHub: true } return wa(p) }这些特性函数接收一个ProjectBuilder并返回新的Project恰好匹配extend的f: (wa) B形状。于是可以通过C.extend逐层叠加// extract以默认配置构建 C.extract(buildProject(myproject)) // { projectName: myproject, projectHasLibrary: false, projectGitHub: false, projectTravis: false } // extend 一层开启 library C.extract(C.extend(buildProject(myproject), hasLibraryB)) // projectHasLibrary: true // 两层叠加 tracks 自动联动 const travisB _.tracks(M, (project) ({ ...M.empty, settingsTravis: project.projectGitHub })) C.extract(C.extend(C.extend(buildProject(github-travis), gitHubB), travisB)) // { projectName: github-travis, projectGitHub: true, projectTravis: true }这里展示了Traced与Comonad的组合威力每个extend层都是对环境的增量改写tracks还能根据前一层的结果自动推导下一层的位置偏移最终由extract在M.empty默认环境上一次成型。7.4 与listen/listens/censor搭配listenC.extract(_.listen(buildProject(myproject)))返回[Project, Settings]可同时拿到构建结果与所用配置listenspipe(buildProject(myproject), _.listens((s) s.settingsTravis))返回[Project, boolean]取出的辅助值是当前是否启用 Traviscensor在读取前改写配置如测试中取反settingsHasLibrary。这四个工具函数让Traced既能读也能看审计当前位置、改覆盖配置、追按相对位置跳转构成一套完整的环境操作 DSL。八、小结与适用场景API类型作用引入版本TracedP, A模型(p: P) A环境读取函数v2.0.0Functor实例对读取结果mapv2.7.0getComonad(monoid)实例构造器基于MonoidP构造Comonad2Cv2.0.0mapmapping柯里化映射v2.0.0flapmapping值翻转应用v2.10.0listenutils取出当前位置v2.0.0listensutils取出位置的派生值v2.0.0censorutils改写当前位置v2.0.0tracksutils按相对位置提取值v2.0.0traced废弃用Functor替代v2.0.0废弃适用场景包括配置驱动的增量构建如测试中的工程脚手架生成、带上下文的审计日志listen/listens、基于默认值逐层覆盖的设置系统censorextract以及任何从可叠加环境中读取派生数据的领域。需要特别说明的是getComonad强依赖MonoidP只有P具备幺元与可结合拼接运算extract读默认位置和extend位置偏移才能成立。如果你想进一步深入可以继续阅读 Traced 源码、模块文档 与配套的 测试用例并结合 Comonad 接口定义 与 Monoid 构造工具 理解其抽象层级。赞分享开发工具【免费下载链接】fp-tsFunctional programming in TypeScript项目地址https://gitcode.com/gh_mirrors/fp/fp-ts点击查看免费下载相关推荐如何快速分析GPS轨迹数据GPXSee终极免费工具完整指南如何快速分析GPS轨迹数据GPXSee终极免费工具完整指南 GPS轨迹分析是户外运动爱好者和专业地理工作者的必备技能但面对众多GPS文件格式你是否感到困惑桌面应用GIS探索 fp-ts函数式编程在 TypeScript 中的力量探索 fp ts函数式编程在 TypeScript 中的力量 是一个由 Giacomo Canti 开发的开源库它为 TypeScript 提供了一套全面的开发工具LeeGo高级组件Union Brick实现复杂界面组合的终极方案LeeGo高级组件Union Brick实现复杂界面组合的终极方案 LeeGo是一个声明式、可配置且高度可重用的Swift UI开发框架让iOS界面开发像搭上一篇终极指南如何高效优化Kubeshark大规模集群流量处理性能下一篇djLint 项目使用教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

天涯杂志源码深度剖析:3步搞定完整示例

天涯杂志源码深度剖析:3步搞定完整示例

天涯杂志源码深度剖析:3步搞定完整示例 别盯着语法书发呆,很多程序员卡在“学会语法却不知怎么搭项目”这一步。 看着文档里的 完整示例 ,心里没底,不知道从哪下手。 今天拆解 天涯杂志 这个经典实战项目,带你从零跑通全流程。…

2026/9/23 13:15:00 阅读更多 →
3步搞定打王者荣耀手写题完整示例

3步搞定打王者荣耀手写题完整示例

3步搞定打王者荣耀手写题完整示例 很多兄弟刚学完Python或Java语法,觉得挺顺溜,一遇到“手写一个打王者荣耀”这种面试题就懵圈了。不是不会写循环,而是不知道怎么把零散的逻辑拼成一个能跑的项目。别慌,这就是典型的“语法会了,项目不会搭”…

2026/9/23 13:13:59 阅读更多 →
娃交VISIO入门到精通:3步搞懂底层逻辑,新手避坑指南

娃交VISIO入门到精通:3步搞懂底层逻辑,新手避坑指南

娃交VISIO入门到精通:3步搞懂底层逻辑,新手避坑指南 刚学完语法,看着满屏的代码发呆,不知道第一行代码该写在哪?别慌,这是90%的新手在从“入门”迈向“精通”路上都会撞上的墙。很多教程只教你怎么敲指令,却没人告诉你怎么把这些指令拼成一个…

2026/9/23 13:13:59 阅读更多 →

最新新闻

Java企业报销系统实战:Spring Boot+Flowable流程驱动开发

Java企业报销系统实战:Spring Boot+Flowable流程驱动开发

简介:本资源是一套完整的Java毕业设计项目——企业报销管理系统,面向计算机专业本科生及Java初学者,聚焦办公自动化场景,解决传统纸质报销流程效率低、信息难共享、审批难追溯等实际问题。压缩包共206个文件,含109个编…

2026/9/23 20:40:00 阅读更多 →
Java在线教育系统源码:生产级Spring Boot教务骨架

Java在线教育系统源码:生产级Spring Boot教务骨架

简介:这是一套基于Java技术栈开发的智能在线教育系统完整源码,面向高校计算机专业学生、Java初中级开发者及教育类应用实践者,旨在帮助学习者掌握Spring Boot全栈开发、在线课堂实时交互、多角色权限管理等核心工程能力。资源共288个文件&…

2026/9/23 20:40:00 阅读更多 →
Delphi调用OpenCV 4.8.1全栈配置指南

Delphi调用OpenCV 4.8.1全栈配置指南

简介:本资源是面向Delphi开发者(尤其适配Delphi 11)的OpenCV快速集成解决方案,专为解决传统OpenCV-Delphi配置繁琐、依赖文件分散、耗时易错等痛点而设计。资源包整合了OpenCV 2.4.13全量适配组件,涵盖114个运行时DLL、…

2026/9/23 20:40:00 阅读更多 →
五行掌教学视频入门到精通,别被伪代码骗了

五行掌教学视频入门到精通,别被伪代码骗了

五行掌教学视频入门到精通,别被伪代码骗了 看了一堆教程还是不会写项目?这是不是你的真实写照? 手里攥着几本大部头,视频刷了几十集,结果一上手写个像样的功能,脑子还是空白。…

2026/9/23 20:40:00 阅读更多 →
RPA在AI获客中的合规边界:拟人化交互与平台风控的技术对抗

RPA在AI获客中的合规边界:拟人化交互与平台风控的技术对抗

一、问题背景 在AI获客场景中,大量动作发生在跨平台场景:发布内容、回复评论、执行任务。这些动作通常依靠RPA(机器人流程自动化)完成。 但RPA的使用面临一个根本矛盾:平台希望用户行为是"人"的,…

2026/9/23 20:39:59 阅读更多 →
codeburn Open Design 提供方深度解析:事件流 JSONL 的会话发现、Token 归因与本地成本核算

codeburn Open Design 提供方深度解析:事件流 JSONL 的会话发现、Token 归因与本地成本核算

【免费下载链接】codeburn Free, local tool to track AI coding token usage and cost across 37 tools and agents (Claude Code, Cursor, Codex, Gemini and more), by model, project, and task. npx codeburn 项目地址: https://gitcode.com/gh_mirrors/co/cod…

2026/9/23 20:38:59 阅读更多 →

日新闻

3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A…

2026/9/23 0:00:23 阅读更多 →
2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我

2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我

2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我 刚把开发环境的显示器从1080P换到2K,跑老项目直接报错,版本升级后 API…

2026/9/23 0:01:25 阅读更多 →
3步搞定美眉图实战项目,告别官方文档抓不住重点

3步搞定美眉图实战项目,告别官方文档抓不住重点

3步搞定美眉图实战项目,告别官方文档抓不住重点 官方文档翻了三遍还是云里雾里?别急,美眉图在实战项目中常被用来做数据可视化,但它的原理比你想的简单。今天咱们直接上手,用一个完整的小项目把美眉图跑通,不再死磕那些冗长的理论说明。…

2026/9/23 0:01:25 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/23 4:55:02 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/23 4:49:06 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/23 9:53:41 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/23 9:53:40 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/23 9:53:40 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/23 9:53:40 阅读更多 →