如何编写一个 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 Task
s of Option
s 然后将被传递给给定的延续。但我马上就需要结果。
这是我的 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));
请注意,tChainT
和 tOfT
分别与 tChain
和 tOf
具有相同的实现。这是因为 TaskT
的底层 monad 来自解析和拒绝处理程序。因此,延续机制为我们处理它。毕竟 Task
只是 Cont
加上一个额外的拒绝延续。
我将 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 Task
s of Option
s 然后将被传递给给定的延续。但我马上就需要结果。
这是我的 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));
请注意,tChainT
和 tOfT
分别与 tChain
和 tOf
具有相同的实现。这是因为 TaskT
的底层 monad 来自解析和拒绝处理程序。因此,延续机制为我们处理它。毕竟 Task
只是 Cont
加上一个额外的拒绝延续。