如何编写一个 TaskT Monad Transformer 来组合异步计算和其他效果?

How to write a TaskT Monad Transformer to compose asynchronous computations with other effects?

我将 monad 转换器构建为 chain/of 的变体,它带有一个额外的参数 - 外部 monad 的类型目录:

const None =
  ({runOption: null, tag: "None", [Symbol.toStringTag]: "Option"});

const Some = x =>
  ({runOption: x, tag: "Some", [Symbol.toStringTag]: "Option"});
  
const optOfT = of => x => of(Some(x));

const optChainT = ({chain, of}) => fmm => mmx =>
  chain(mx => {
    switch (mx.tag) {
      case "None": return of(None);
      case "Some": return fmm(mx.runOption);
    }
  }) (mmx);

const arrOf = x => [x];

const arrChain = fm => xs =>
  xs.reduce((acc, x) => arrPushFlat(acc) (fm(x)), []);

const arrPushFlat = xs => ys => {
  ys.forEach(x =>
    xs.push(x));

  return xs;
};

const xs = [Some("foo"), None, Some("bar")];

console.log(
  optChainT({chain: arrChain, of: arrOf})
    (s => [Some(s.toUpperCase())]) (xs)); // [Some("FOO"), None, Some("BAR")]

所以基本上一个 transformer 是两个 monad 的手写组合,即它需要两个 monad 和 returns 一个新的复合 monad,因此它本身是可组合的。欢迎使用可组合效果。

但是当懒惰出现在 table 上时,我无法全神贯注于 monad 转换器。如果我想为 [Task<Option<a>, Error>] 创建一个 monad 转换器怎么办?我需要一个用于异步任务的转换器,即 tChainT,但这个运算符会是什么样子?

这是一个机械实现,它 (AFAIK) 说明了为什么 monad 不能以一般方式组合:

const tChainT = ({chain, of}) => fmm => mmx =>
  chain(mx =>
    tChain(fmm) (mx) // A
      ) (mmx); 

A returns 一个 Task 当 运行 最终会产生一个 Array of Tasks of Options 然后将被传递给给定的延续。但我马上就需要结果。

这是我的 Task 实现中与问题相关的部分:

const Task = k =>
  ({runTask: (res, rej) => k(res, rej), [Symbol.toStringTag]: "Task"});

const tChain = fm => mx =>
  Task((res, rej) => mx.runTask(x => fm(x).runTask(res, rej), rej));

const tOf = x => Task((res, rej) => res(x));

您问题中的 tChainT 函数没有按预期工作,因为它的类型错误。

const tChainT = ({chain, of}) => fmm => mmx =>
  chain(mx =>
    tChain(fmm) (mx) // A
      ) (mmx);

// can be simplified to

const tChainT = ({ chain }) => fmm => chain(tChain(fmm));

// tChain  ::                   (a -> Task e b) -> Task e a -> Task e b
//                                                 |______|    |____|
//                                                    |          |
// chain   :: Monad    m     =>                      (a     ->   m    b) -> m        a      ->   m    b
//                    _|__                                                 _|__    __|___       _|__
//                   |    |                                               |    |  |      |     |    |
// tChainT :: Monad (Task e) => (a -> Task e b) ->                        Task e (Task e a) -> Task e b

your answer 中的 tChainT 函数也有错误的类型。

// tChainT :: Monad m => (a -> Task e b) -> m (Task e a) -> m (Task e b)
const tChainT = ({chain, of}) => fm => mmx =>
  chain(mx =>
    of(tChain(fm) (mx))) (mmx); // ***A***

请注意 fm 的 return 类型是 Task e b 而不是 m (Task e b)。因此,tChainT 不是 return 有效的单子绑定函数。事实上,不可能创建 Monad m => (a -> m (Task e b)) -> m (Task e a) -> m (Task e b) 类型的函数,因为 m (Task e a)TaskT e monad 转换器的错误结构。正确的结构如下

// newtype TaskT e m a = TaskT { runTaskT :: (a -> m (), b -> m ()) -> m () }
const TaskT = runTaskT => ({ [Symbol.toStringTag]: "TaskT", runTaskT });

// tChainT :: (a -> TaskT e m b) -> TaskT e m a -> TaskT e m b
const tChainT = f => m => TaskT((res, rej) =>
    m.runTaskT(x => f(x).runTaskT(res, rej), rej));

// tOfT :: a -> TaskT e m a
const tOfT = x => TaskT((res, rej) => res(x));

请注意,tChainTtOfT 分别与 tChaintOf 具有相同的实现。这是因为 TaskT 的底层 monad 来自解析和拒绝处理程序。因此,延续机制为我们处理它。毕竟 Task 只是 Cont 加上一个额外的拒绝延续。