You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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:

  1. 失败值构造器:如Option的none、Either的left,用于生成失败状态的Monad;
  2. 映射/匹配操作:提取成功状态下的元组,将每个元素包装回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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 17:58:26