在 F# 中获取第一个有效结果的计算表达式
A computation expression to get the first valid result out, in F#
我怎样才能以干净的方式实现这样的目标?
让我们想象一下这个简单的代码:
let a () = checkSomeStuff (); None
let b () = do Something (); Some "thing"
let c () = checkSomethingElse (); None
"getOne" {
do! a()
do! b()
do! c()
}
它会 return 第一个“Some”。
我可以通过使用 Result 来实现这个确切的行为,我会 return 通过一个 Error 的值并继续通过 Ok,但这不可读/很好:
let a () = checkSomeStuff (); Ok ()
let b () = do Something (); Error "thing"
let c () = checkSomethingElse (); Ok ()
result {
do! a()
do! b()
do! c()
}
这可行,但我希望在不误用 Result 类型的情况下实现这一点。可以用现有的表达式来完成吗?
前段时间,我写了一篇关于 imperative computation expression builder 的 post 文章,它按照这些思路做了一些事情。您可以将计算表示为 option-returning 函数:
type Imperative<'T> = unit -> option<'T>
在计算生成器中,最主要的是 Combine
表示操作顺序的操作,但您还需要一些其他操作才能使其工作:
type ImperativeBuilder() =
member x.ReturnFrom(v) = v
member x.Return(v) = (fun () -> Some(v))
member x.Zero() = (fun () -> None)
member x.Delay(f:unit -> Imperative<_>) =
(fun () -> f()())
member x.Combine(a, b) = (fun () ->
match a() with
| Some(v) -> Some(v)
| _ -> b() )
let imperative = new ImperativeBuilder()
然后您可以重新实现您的示例 - return 一个值,您只需使用 return
,但您需要使用 return!
组合各个操作,因为构建器不支持do!
:
let a () = imperative { printfn "one" }
let b () : Imperative<string> = imperative { return "result" }
let c () = imperative { printfn "two" }
let f = imperative {
return! a()
return! b()
return! c()
}
f()
您不需要计算表达式。 F# 有一个名为 Seq.tryPick
的 built-in 函数,它将给定函数应用于序列的连续元素,返回第一个 Some
结果(如果有)。您可以使用 tryPick
来定义 getOne
,如下所示:
let getOne fs =
fs |> Seq.tryPick (fun f -> f ())
用你的例子试试:
let a () = checkSomeStuff ();
let b () = Something ();
let c () = checkSomethingElse ();
let x = getOne [ a; b; c ]
printfn "%A" x // Some "thing"
您可以创建一个函数来执行您想要的操作。但是你必须考虑清楚你想做什么。
所以,你的逻辑是。
- 你执行了一个 return 是
option
的函数
- 然后你检查
option
。如果它是 None
你执行另一个函数,如果它是 Some
你 return 值。
像这样的函数可能如下所示:
let getSome f opt =
match opt with
| None -> f ()
| Some x -> Some x
有了这样的功能,你就可以写了。 ***
let x =
checkSomeStuff ()
|> getSome (fun _ -> Something () )
|> getSome checkSomethingElse
但后来我想,嗯……getSome
没有更好的名字吗?在某种程度上我想说:
Execute some code and check if it is Some
, or else pick the next thing.
考虑到这一点,我想。嗯....不是已经有Option.orElse
了吗?是的!有!还有一个 Option.orElseWith
功能,可以更好地满足您的需求。所以现在,你可以写了。
let y =
checkSomeStuff ()
|> Option.orElseWith (fun _ -> Something () )
|> Option.orElseWith checkSomethingElse
如果你有side-effects的功能,那么你应该使用Option.orElseWith
,否则,你可以起诉Option.orElse
***:我假设您定义了以下函数
let checkSomeStuff () =
None
let Something () =
Some "thing"
let checkSomethingElse () =
None
我怎样才能以干净的方式实现这样的目标?
让我们想象一下这个简单的代码:
let a () = checkSomeStuff (); None
let b () = do Something (); Some "thing"
let c () = checkSomethingElse (); None
"getOne" {
do! a()
do! b()
do! c()
}
它会 return 第一个“Some”。
我可以通过使用 Result 来实现这个确切的行为,我会 return 通过一个 Error 的值并继续通过 Ok,但这不可读/很好:
let a () = checkSomeStuff (); Ok ()
let b () = do Something (); Error "thing"
let c () = checkSomethingElse (); Ok ()
result {
do! a()
do! b()
do! c()
}
这可行,但我希望在不误用 Result 类型的情况下实现这一点。可以用现有的表达式来完成吗?
前段时间,我写了一篇关于 imperative computation expression builder 的 post 文章,它按照这些思路做了一些事情。您可以将计算表示为 option-returning 函数:
type Imperative<'T> = unit -> option<'T>
在计算生成器中,最主要的是 Combine
表示操作顺序的操作,但您还需要一些其他操作才能使其工作:
type ImperativeBuilder() =
member x.ReturnFrom(v) = v
member x.Return(v) = (fun () -> Some(v))
member x.Zero() = (fun () -> None)
member x.Delay(f:unit -> Imperative<_>) =
(fun () -> f()())
member x.Combine(a, b) = (fun () ->
match a() with
| Some(v) -> Some(v)
| _ -> b() )
let imperative = new ImperativeBuilder()
然后您可以重新实现您的示例 - return 一个值,您只需使用 return
,但您需要使用 return!
组合各个操作,因为构建器不支持do!
:
let a () = imperative { printfn "one" }
let b () : Imperative<string> = imperative { return "result" }
let c () = imperative { printfn "two" }
let f = imperative {
return! a()
return! b()
return! c()
}
f()
您不需要计算表达式。 F# 有一个名为 Seq.tryPick
的 built-in 函数,它将给定函数应用于序列的连续元素,返回第一个 Some
结果(如果有)。您可以使用 tryPick
来定义 getOne
,如下所示:
let getOne fs =
fs |> Seq.tryPick (fun f -> f ())
用你的例子试试:
let a () = checkSomeStuff ();
let b () = Something ();
let c () = checkSomethingElse ();
let x = getOne [ a; b; c ]
printfn "%A" x // Some "thing"
您可以创建一个函数来执行您想要的操作。但是你必须考虑清楚你想做什么。
所以,你的逻辑是。
- 你执行了一个 return 是
option
的函数
- 然后你检查
option
。如果它是None
你执行另一个函数,如果它是Some
你 return 值。
像这样的函数可能如下所示:
let getSome f opt =
match opt with
| None -> f ()
| Some x -> Some x
有了这样的功能,你就可以写了。 ***
let x =
checkSomeStuff ()
|> getSome (fun _ -> Something () )
|> getSome checkSomethingElse
但后来我想,嗯……getSome
没有更好的名字吗?在某种程度上我想说:
Execute some code and check if it is
Some
, or else pick the next thing.
考虑到这一点,我想。嗯....不是已经有Option.orElse
了吗?是的!有!还有一个 Option.orElseWith
功能,可以更好地满足您的需求。所以现在,你可以写了。
let y =
checkSomeStuff ()
|> Option.orElseWith (fun _ -> Something () )
|> Option.orElseWith checkSomethingElse
如果你有side-effects的功能,那么你应该使用Option.orElseWith
,否则,你可以起诉Option.orElse
***:我假设您定义了以下函数
let checkSomeStuff () =
None
let Something () =
Some "thing"
let checkSomethingElse () =
None