为什么用 ap 减少一个 Option 数组会导致 None?
Why does reducing an Array of Option with ap result in None?
我正在尝试这样做是为了学习。但是我无法让它工作(因为你正在阅读这篇文章,所以很惊讶 :))
算法:
- 将 valid/invalid 个对象的混合列表作为输入
- 从每个对象(或空值)中提取所需的属性
- 转换为选项列表
- 通过使用对聚合类型进行操作的函数来减少选项列表。
我无法通过优雅地提升 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
在减速器中。
看看 ap
对 Option
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
可以执行。
我正在尝试这样做是为了学习。但是我无法让它工作(因为你正在阅读这篇文章,所以很惊讶 :))
算法:
- 将 valid/invalid 个对象的混合列表作为输入
- 从每个对象(或空值)中提取所需的属性
- 转换为选项列表
- 通过使用对聚合类型进行操作的函数来减少选项列表。
我无法通过优雅地提升 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
在减速器中。
看看 ap
对 Option
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
可以执行。