fp-ts中A.sequenceT的逆操作是什么?是否有通用实现?
关于
sequenceT逆操作的通用实现与相关概念 A.sequenceT操作可将一系列类型化Monad转换为类型化元组的Monad,例如以下Option示例:
const sequenceO = A.sequenceT(O.Apply); const o1: Option<number> = O.some(1); const o2: Option<string> = O.some("1"); const o12: Option<[number, string]> = sequenceO(o1, o2);
该操作的逆操作最佳表示是什么?我针对Option实现的特例如下:
const unsequenceO = O.fold<[number, string], [Option<number>, Option<string>]>( () => [O.none, O.none], ([s1, s2]) => [O.some(s1), O.some(s2)] ); const [u1, u2] = unsequenceO(o12);
但我想知道是否存在相关概念及更通用的实现方式。
逆操作的通用概念与实现
这个逆操作通常被称为unsequence(逆序列),本质是将包含元组的Monad,分解为多个对应元素类型的Monad组成的元组。核心逻辑可概括为:
- 若原Monad处于“失败”状态(如Option的
none),返回全失败状态的Monad元组; - 若原Monad处于“成功”状态(如Option的
some),则拆分元组中的每个元素,分别包装回对应Monad后组成元组返回。
通用实现思路
对于符合Monad规范的类型,可基于两个核心操作实现通用的unsequenceT:
- 失败值构造器:如Option的
none、Either的left,用于生成失败状态的Monad; - 映射/匹配操作:提取成功状态下的元组,将每个元素包装回Monad并组成元组。
以TypeScript结合fp-ts为例,通用的二元元组unsequenceT实现如下:
import { HKT, Kind, Kind2, URIS, URIS2 } from 'fp-ts/HKT'; import { Option, none, some } from 'fp-ts/Option'; import { Either, left, right } from 'fp-ts/Either'; // 二元元组的通用unsequence实现 function unsequenceT2<M extends URIS>( fail: <A>() => Kind<M, A>, succeed: <A>(a: A) => Kind<M, A> ): <A, B>(ma: Kind<M, [A, B]>) => [Kind<M, A>, Kind<M, B>]; function unsequenceT2<M extends URIS2>( fail: <E, A>(e: E) => Kind2<M, E, A>, succeed: <E, A>(a: A) => Kind2<M, E, A> ): <E, A, B>(ma: Kind2<M, E, [A, B]>) => [Kind2<M, E, A>, Kind2<M, E, B>]; function unsequenceT2( fail: (...args: any[]) => any, succeed: (...args: any[]) => any ) { return <T>(ma: any): any => { // 基于Monad的_tag标识做模式匹配 if ('_tag' in ma) { switch (ma._tag) { case 'None': return [fail(), fail()]; case 'Some': const [a, b] = ma.value; return [succeed(a), succeed(b)]; case 'Left': return [fail(ma.left), fail(ma.left)]; case 'Right': const [x, y] = ma.right; return [succeed(x), succeed(y)]; default: throw new Error('不支持的Monad类型'); } } // 兼容无_tag但有fold方法的Monad return ma.fold( () => [fail(), fail()], ([a, b]) => [succeed(a), succeed(b)] ); }; } // Option类型的实例化 const unsequenceO2 = unsequenceT2(none, some); // 使用示例 const oSuccess: Option<[number, string]> = some([1, "1"]); const [u1, u2] = unsequenceO2(oSuccess); // [some(1), some("1")] const oFail: Option<[number, string]> = none; const [uFail1, uFail2] = unsequenceO2(oFail); // [none, none] // Either类型的实例化 const unsequenceE2 = unsequenceT2(left, right); // 使用示例 const eSuccess: Either<string, [number, string]> = right([1, "1"]); const [e1, e2] = unsequenceE2(eSuccess); // [right(1), right("1")] const eFail: Either<string, [number, string]> = left("出错了"); const [eFail1, eFail2] = unsequenceE2(eFail); // [left("出错了"), left("出错了")]
扩展说明
- 该实现可轻松扩展到三元、四元等更多元的元组,只需调整函数的返回元组长度及内部拆分逻辑;
- 不同Monad的核心逻辑一致:失败时返回全失败元组,成功时拆分包装;
- 在fp-ts等函数式库中虽无内置的
unsequenceT,但可基于现有Monad的fold或模式匹配能力快速实现,或通过泛型HKT封装通用版本。
内容的提问来源于stack exchange,提问作者Carsten
相关产品推荐
相关产品推荐

