1

背景

我正在使用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,但我不明白为什么。

问题

  1. 我应该什么时候使用bimap,什么时候应该使用fold
  2. 它们之间的主要区别是什么?
4

2 回答 2

2

让我们首先检查它们各自的类型签名:

bimap :: (a -> c) -> (b -> d) -> Future a b -> Future c d
fold  :: (a -> c) -> (b -> c) -> Future a b -> Future d c

差异非常微妙,但可见。有两个主要区别:

  1. 第二个参数的返回值不同:在bimap中,两个函数都允许返回不同的类型。在fold中,两个函数必须返回相同类型的值。
  2. 最终的返回值不同:在bimap中,您返回一个 Future,其中拒绝包含从左函数返回的类型的值,而解析包含从右函数返回的类型的值。在fold中,拒绝端包含一个尚未被限制的全新类型变量,而解决端包含两个函数返回的类型的值。

这是一个相当拗口的,并且可能有点难以解析。我将尝试在图表中将其可视化。

对于bimap,它如下所示。两个分支不交互:

             rej(x)  res(y)
                 |       |
                 |       |
bimap(f)(g):   f(x)    g(y)
                 |       |
                 V       V

对于fold,拒绝分支类型“停止”,并且解析分支将继续返回值 fromf(x) 返回值 from 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)
于 2018-07-19T15:59:56.917 回答
1

可以在一个步骤中bimap同时使用映射拒绝解决Future,并且将通过新的计算保持拒绝解决

另一方面,fold将同时处理拒绝解决 以始终产生解决方案(您将两种情况都折叠到解决方案中)。可以使用fold将两个结果包装成另一种类型(例如Future Either a b)或将任何分支视为成功

因此,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)

注意 howfold给了我一个完整的保证eventualNumber2是一个resolution,所以我使用Future.valuewhich 只处理分辨率!

于 2018-07-19T15:21:11.240 回答