为什么用 ap 减少一个 Option 数组会导致 None?

Why does reducing an Array of Option with ap result in None?

我正在尝试这样做是为了学习。但是我无法让它工作(因为你正在阅读这篇文章,所以很惊讶 :))

算法:

我无法通过优雅地提升 sumAggregates 来使类型工作,所以我尝试使用 pipe, ap 来做到这一点。但我想看看我们如何正确提升 sumAggregates 以在 reduce 中使用。

请注意,我的目标不是以不同的方式获得正确的结果,而是了解我的实施失败的原因。

  type Actionable = {
    action?: string
  }
  type Aggregate = {
    allowed: number,
    blocked: number
  }

  const emptyAggregate: Aggregate = {
    allowed: 0,
    blocked: 0
  }

  const list: Actionable[] = [ { action: 'block'}, { }, { action: 'block'}, { }, { action: 'allow'}]

  const extractAction = (a: Actionable) => a.action
  const stringToAggregator = (str: string): Aggregate => {
    return {
      allowed: str === 'allow' ? 1 : 0,
      blocked: str === 'block' ? 1 : 0,
    }
  }
  const sumAggregates = (a: Aggregate) => (b: Aggregate): Aggregate => {
    return {
      allowed: a.allowed + b.allowed,
      blocked: b.blocked + b.blocked,
    }
  }

  const totals: O.Option<Aggregate> = pipe(
    list,
    A.map(extractAction),
    A.map(O.fromNullable),
    A.map(O.map(stringToAggregator)),
    A.reduce(
      O.some(emptyAggregate),
      (a: O.Option<Aggregate>, b: O.Option<Aggregate>) => {
        return pipe(O.of(sumAggregates), O.ap(a), O.ap(b))
      }
    )
  )

Returns None 而不是 some({allowed: 1, blocked: 2})

你最终得到 None 因为 reduce 列表中的元素之一是 None,你将那个 None 传递给 O.ap 在减速器中。

看看 apOption here 的定义。当提供 None 并且 ap 的结果变为 None 时,提升计算不会被调用。那么对于以下最基本的计算,您期望结果是什么?

import * as O from 'fp-ts/Option'
import {identity, pipe} from 'fp-ts/function'

const result: O.Option<number> = pipe(
  O.some(identity),
  O.ap(O.none),
)

应该是None吧?这就是 Option 的 apply/applicative 实例众所周知的效果。因此,在您的 reduce 函数中,一旦遇到 None 并将其传递给 ap,累加器将在 None 情况下结束,并且不会进一步应用 sumAggregates 可以执行。