如何避免在F#中将某些内容双重包装成Some

3

I have the following code:

let f g x = if x < 0 then None else Some(g x)

除了 f 函数可能或可能不返回 Option 之外,g 函数也是如此。由于 f 是通用的,并且没有任何通用约束,因此我最终可能会得到 Some(Some(z)) 的结果。事实上,我只想要 NoneSome(z) 中的一个。我该如何避免双重包装(最好不对 g 强加约束)?

5个回答

3
> let f g x = if x < 0 then None else Some(g x)

val f : g:(int -> 'a) -> x:int -> 'a option
f返回'a option,这意味着它可以返回Some zSome (Some y)等。 f可以使用任意数量的嵌套Some返回结果,具体取决于g的类型。
如果我理解这个问题正确,它是关于将嵌套的Some合并的函数。它可以手动编写:
let collapseOptions x =
    match x with
    | Some (Some y) -> y
    | _ -> None

如果这个问题是关于折叠所有嵌套的Some函数,我希望看到它的签名 :)

感谢,“collapseOptions” 最多只折叠两级,这正是我所需要的。 - Trident D'Gao

3
在这种情况下,你有两个选择。要么将约束条件g的类型设为'a -> Option<'b>,要么单独处理嵌套的选项值。第一个版本:
let f (g: int -> Option<'b>) (x: int) = 
    if x < 0 then None else g x

在这种情况下,每当您希望将类型为int -> 'b的普通函数传递给f时,您需要将它提升为类型为int -> Option<int>的函数。以下是一个示例:
// Simple function of type: int -> int
let plusOne = (+) 1
let y = f x (pluseOne >> Some)

第二种选择是保留原始定义:
let f (g: int -> 'b) (x: int) = if x < 0 then None else Some (g x)

如果需要的话,可以简单地折叠结果。这可以通过Option.bind id轻松实现,如下所示:

// This function has type: int -> Option<int>
let posPlusOne n = if n < 0 then None else Some (n + 1)
let y = f x posPlusOne |> Option.bind id

3
使用 Option.bind
let f g x = if x < 0 then None else Option.bind g (Some x)

它如何帮助折叠嵌套的“Some”? - ДМИТРИЙ МАЛИКОВ
@ДМИТРИЙ МАЛИКОВ:Option.bind: ('a -> 'b option) -> 'a option -> 'b option,它返回g()的结果,仅适用于g返回一个选项。 - kwingho
1
这种方法要求 g 返回 Option 'a,而 bind g (Some x) 就是 g x - ДМИТРИЙ МАЛИКОВ

2

2
最简单的解决方法是使用匹配。
if x < 0 then
    None
else match g x with
     |Some(t) -> Some(t)
     |None -> None

3
看起来这段代码只是执行了 else g x 而没有更多的操作来解决原问题 :) - ДМИТРИЙ МАЛИКОВ
这种方法要求g返回Option 'a,虽然在某些情况下这可能是一种解决方案,但似乎会增加不必要的复杂性,因为只有一个接受 Option 'a 的函数可以用作g。 我的意思是,如果我想使用一个接受直接'a的函数g',那么我必须将其包装成let g x = Some(g' x)的形式。 - Trident D'Gao

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接