在您所描述的功能世界中,通常称为sequence。
这是交换泛型类型顺序的概念:
- 从
List<Option<int>> 到Option<List<int>>
这是一个通用的方法:
module Option =
let (>>=) r f = Option.bind f r
let rtn v = Some v
let traverseList f ls =
let folder head tail = f head >>= (fun h -> tail >>= (fun t -> h::t |> rtn))
List.foldBack folder ls (rtn List.empty)
let sequenceList ls = traverseList id ls
// val traverseList : ('a -> 'b option) -> 'a list -> 'b list option
// val sequenceList : 'a option list -> 'a list option
这里sequenceList 的类型签名为'a option list -> 'a list option,也可以表示为List<Option<'a>> -> Option<List<'a>>。这正是您想要的。
但是,但是,但是……与其他答案相比,这个答案似乎更加复杂。有什么意义?
关键是这个解决方案是通用的,可以应用于任何一元(或应用)类型。例如与Option 类似的泛型类型是Result,解决方案与之前几乎相同:
module Result =
let (>>=) r f = Result.bind f r
let rtn v = Ok v
let traverseList f ls =
let folder head tail = f head >>= (fun h -> tail >>= (fun t -> h::t |> rtn))
List.foldBack folder ls (rtn List.empty)
let sequenceList ls = traverseList id ls
// val traverseList : ('a -> Result<'b,'c>) -> 'a list -> Result<'b list,'c>
// val sequenceList : Result<'a,'b> list -> Result<'a list,'b>
看到了吗?除了第 2 行和第 3 行之外,其余部分相同。
这使您可以抽象概念并将其置于脑海中。将来,当您编程时,这将发生很多次,您需要遍历泛型类型,而类型最终会根据您的需要而反转。
但是没有必要重新发明轮子。掌握了如何创建sequence(和traverse)的知识后,您可以在任何一元类型或应用类型需要时将其伸入工具包中,无论是Async、Result、@ 987654336@等
如果您想深入了解,请访问此网站,该网站以清晰的图形和示例解释概念:https://fsharpforfunandprofit.com/posts/elevated-world-4/