javascript - 如何编写一个 TaskT Monad Transformer 来组合具有其他效果的异步计算?
问题描述
我将 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")]
所以基本上一个转换器是两个单子的手写组合,即它需要两个单子并返回一个新的复合单子,因此它本身是可组合的。欢迎使用可组合效果。
但是当懒惰出现在桌面上时,我无法将我的头包裹在 monad 转换器上。如果我想为 monad 创建一个转换器[Task<Option<a>, Error>]
怎么办?我需要一个用于异步任务的转换器,即 a tChainT
,但是这个运算符会是什么样子?
这是一个机械实现(AFAIK)说明了为什么单子不能以一般方式组合:
const tChainT = ({chain, of}) => fmm => mmx =>
chain(mx =>
tChain(fmm) (mx) // A
) (mmx);
行A
返回 a Task
,当运行时最终会产生一个s 的Array
s ,然后将传递给给定的延续。但我需要马上得到结果。Task
Option
这是我的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
您答案中的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
是Task e b
而不是m (Task e b)
。因此,tChainT
不返回有效的一元绑定函数。事实上,创建该类型的函数是不可能的,Monad m => (a -> m (Task e b)) -> m (Task e a) -> m (Task e b)
因为monad 转换m (Task e a)
器的结构错误。TaskT e
正确的结构如下。
// 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
具有相同的实现。这是因为底层的 monad来自解决和拒绝处理程序。因此,延续机制为我们处理它。毕竟只是有一个额外的继续拒绝。tChain
tOf
TaskT
Task
Cont
推荐阅读
- c# - System.Drawing.Common DrawString 无法在 Linux 中正确呈现文本
- python - 在我的亚马逊卖家页面中使用 Beautiful Soup 解析未发货的订单;找不到html标签
- linux - 如何知道哪个内核发送了处理器间中断?
- python - 按几列排序唯一,然后合并不匹配列中的值
- javascript - 如何让我的文字跟随我在 Phaser 3 中的玩家?
- reactjs - 函数已绑定,但仍然出现 Uncaught TypeError: Cannot read property 'name' of undefined
- php - 为什么下拉列表中的选项值不显示?
- react-native - 如何在钩子中渲染之前调用函数反应原生?
- listview - 如何在颤动中删除PageView页面之间的空间
- python - 将 DataFrame A 中的索引与 DataFrame B 的列号匹配