Fluture bimap 和 fold,有什么区别,我应该什么时候使用它们?
Fluture bimap and fold, what is the difference and when should I use them?
背景
我正在使用 Fluture 抽象 Futures。
假设我有一个发出 GET 请求的函数。这个函数可以成功也可以失败。
发出请求后,如果成功,则打印一条消息,如果失败,则记录错误并执行命令。
axios.get(endpoint, { timeout: timeoutMs })
.fold(
err =>
logger.errorAsync( err )
.chain( ( ) => cmd.getAsync("pm2 restart app")),
response => logger.infoAsync( "Great success!" )
);
研究
我一直在阅读 API,我发现 bimap
和 fold
都对成功和错误应用了一个函数:
bimap:将左函数映射到拒绝值,或将右函数映射到分辨率值,具体取决于哪个存在。
fold:将左函数应用于拒绝值,或将右函数应用于分辨率值,具体取决于哪个存在,并用结果进行解析。
问题
如果你眼尖,你就会知道我的例子不有效。我需要使用 bimap
,但我不明白为什么。
问题
- 什么时候应该使用
bimap
,什么时候应该使用fold
?
- 它们之间的主要区别是什么?
一个人可能会使用 bimap
在一个步骤中同时映射 rejection 和 resolution,并且 Future
将保持 rejected 或 resolved 并进行新的计算。
另一方面,fold
将同时处理拒绝和解决以始终产生 resolution(您将两种情况合并为 resolution 一种情况)。人们会使用 fold
将两个结果包装成另一种类型(例如 Future Either a b
)或将任何分支视为 successful.
因此,bimap
与 fold
不同,因为第一个映射两种情况,第二个将任一情况转换为 分辨率。
样本:bimap
:
const flag = true
const eventualNumber1 = !flag ? Future.reject (1) : Future.of (2)
const eventualNumber2 = Future.bimap (x => x * 2) (x => x + 1) (eventualNumber1)
// it'll output 3.
Future.fork (console.log) (console.log) (eventualNumber2)
样本:fold
:
const flag = false
const eventualNumber1 = !flag ? Future.reject (1) : Future.of (2)
const eventualNumber2 = Future.fold (x => x * 2) (x => x + 1) (eventualNumber1)
// It'll output 2 even when the Future represents a rejection
Future.value (console.log) (eventualNumber2)
请注意 fold
如何完全保证 eventualNumber2
是 分辨率 ,因此我使用仅处理分辨率的 Future.value
!
让我们首先检查一下它们各自的类型签名:
bimap :: (a -> c) -> (b -> d) -> Future a b -> Future c d
fold :: (a -> c) -> (b -> c) -> Future a b -> Future d c
差异非常细微,但很明显。有两个主要区别:
- 第二个参数的return值不同:在
bimap
中,两者
允许函数 return 不同的类型。在 fold
中,两个函数
必须 return 相同类型的值。
- 最终的 return 值不同:在
bimap
中,你得到一个 Future,其中
拒绝包含来自左侧函数的 returned 类型的值,
并且分辨率包含从右边开始 returned 类型的值
功能。在fold
中,拒绝端包含一个全新的类型变量,它
尚未受到限制,决议方包含的价值
键入 return 由 both 函数编辑。
这太啰嗦了,可能有点难以解析。我将尝试在图表中将其可视化。
对于bimap
,它看起来像下面这样。两个分支不交互:
rej(x) res(y)
| |
| |
bimap(f)(g): f(x) g(y)
| |
V V
对于fold
,拒绝分支类型为"stops",resoltion分支将
继续 f(x)
的 return 值 或 g(y)
:
的 return 值
rej(x) res(y)
| |
| |
fold(f)(g): -> f(x)*g(y)
|
V
您可以随时使用 bimap
更改拒绝原因和
同时分辨率值。做 bimap (f) (g)
就像做
compose (mapRej (f)) (map (g))
.
您可以随时使用 fold
将您的拒绝移至解决方案中
分支。在您的情况下,这就是您想要的。你的例子没有的原因
工作是因为你最终得到了一个未来的未来,你必须
展平:
axios.get(endpoint, { timeout: timeoutMs })
.fold(
err =>
logger.errorAsync( err )
.chain( ( ) => cmd.getAsync("pm2 restart app")),
response => logger.infoAsync( "Great success!" )
)
.chain(inner => inner); //<-- Flatten
扁平化 Monad 在函数式编程中很常见,通常
称为 join
,可以像这样实现:
const join = chain(x => x)
背景
我正在使用 Fluture 抽象 Futures。
假设我有一个发出 GET 请求的函数。这个函数可以成功也可以失败。
发出请求后,如果成功,则打印一条消息,如果失败,则记录错误并执行命令。
axios.get(endpoint, { timeout: timeoutMs })
.fold(
err =>
logger.errorAsync( err )
.chain( ( ) => cmd.getAsync("pm2 restart app")),
response => logger.infoAsync( "Great success!" )
);
研究
我一直在阅读 API,我发现 bimap
和 fold
都对成功和错误应用了一个函数:
bimap:将左函数映射到拒绝值,或将右函数映射到分辨率值,具体取决于哪个存在。
fold:将左函数应用于拒绝值,或将右函数应用于分辨率值,具体取决于哪个存在,并用结果进行解析。
问题
如果你眼尖,你就会知道我的例子不有效。我需要使用 bimap
,但我不明白为什么。
问题
- 什么时候应该使用
bimap
,什么时候应该使用fold
? - 它们之间的主要区别是什么?
一个人可能会使用 bimap
在一个步骤中同时映射 rejection 和 resolution,并且 Future
将保持 rejected 或 resolved 并进行新的计算。
另一方面,fold
将同时处理拒绝和解决以始终产生 resolution(您将两种情况合并为 resolution 一种情况)。人们会使用 fold
将两个结果包装成另一种类型(例如 Future Either a b
)或将任何分支视为 successful.
因此,bimap
与 fold
不同,因为第一个映射两种情况,第二个将任一情况转换为 分辨率。
样本:bimap
:
const flag = true
const eventualNumber1 = !flag ? Future.reject (1) : Future.of (2)
const eventualNumber2 = Future.bimap (x => x * 2) (x => x + 1) (eventualNumber1)
// it'll output 3.
Future.fork (console.log) (console.log) (eventualNumber2)
样本:fold
:
const flag = false
const eventualNumber1 = !flag ? Future.reject (1) : Future.of (2)
const eventualNumber2 = Future.fold (x => x * 2) (x => x + 1) (eventualNumber1)
// It'll output 2 even when the Future represents a rejection
Future.value (console.log) (eventualNumber2)
请注意 fold
如何完全保证 eventualNumber2
是 分辨率 ,因此我使用仅处理分辨率的 Future.value
!
让我们首先检查一下它们各自的类型签名:
bimap :: (a -> c) -> (b -> d) -> Future a b -> Future c d
fold :: (a -> c) -> (b -> c) -> Future a b -> Future d c
差异非常细微,但很明显。有两个主要区别:
- 第二个参数的return值不同:在
bimap
中,两者 允许函数 return 不同的类型。在fold
中,两个函数 必须 return 相同类型的值。 - 最终的 return 值不同:在
bimap
中,你得到一个 Future,其中 拒绝包含来自左侧函数的 returned 类型的值, 并且分辨率包含从右边开始 returned 类型的值 功能。在fold
中,拒绝端包含一个全新的类型变量,它 尚未受到限制,决议方包含的价值 键入 return 由 both 函数编辑。
这太啰嗦了,可能有点难以解析。我将尝试在图表中将其可视化。
对于bimap
,它看起来像下面这样。两个分支不交互:
rej(x) res(y)
| |
| |
bimap(f)(g): f(x) g(y)
| |
V V
对于fold
,拒绝分支类型为"stops",resoltion分支将
继续 f(x)
的 return 值 或 g(y)
:
rej(x) res(y)
| |
| |
fold(f)(g): -> f(x)*g(y)
|
V
您可以随时使用 bimap
更改拒绝原因和
同时分辨率值。做 bimap (f) (g)
就像做
compose (mapRej (f)) (map (g))
.
您可以随时使用 fold
将您的拒绝移至解决方案中
分支。在您的情况下,这就是您想要的。你的例子没有的原因
工作是因为你最终得到了一个未来的未来,你必须
展平:
axios.get(endpoint, { timeout: timeoutMs })
.fold(
err =>
logger.errorAsync( err )
.chain( ( ) => cmd.getAsync("pm2 restart app")),
response => logger.infoAsync( "Great success!" )
)
.chain(inner => inner); //<-- Flatten
扁平化 Monad 在函数式编程中很常见,通常
称为 join
,可以像这样实现:
const join = chain(x => x)