I have an asynchronous method I'm writing which is supposed to asynchronously query for a port until it finds one, or time out at 5 minutes;
member this.GetPort(): Async<Port> = this._GetPort(DateTime.Now)
member this._GetPort(startTime: DateTime): Async<Port> = async {
match this._TryGetOpenPort() with
| Some(port) -> port
| None -> do
if (DateTime.Now - startTime).TotalMinutes >= 5 then
raise (Exception "Unable to open a port")
else
do! Async.Sleep(100)
let! result = this._GetPort(startTime)
result}
member this._TryGetOpenPort(): Option<Port> =
// etc.
However, I'm getting some strange type inconsistencies in _GetPort
; the function says I'm returning a type of Async<unit>
instead of Async<Port>
.
CodePudding user response:
It's a little unintuitive, but way to make your code work would be this:
member private this.GetPort(startTime: DateTime) =
async {
match this.TryGetOpenPort() with
| Some port ->
return port
| None ->
if (DateTime.Now - startTime).TotalMinutes >= 5 then
raise (Exception "Unable to open a port")
do! Async.Sleep(100)
let! result = this.GetPort(startTime)
return result
}
member private this.TryGetOpenPort() = failwith "yeet" // TODO
I took the liberty to clean up a few things and make the member private, since that seems to be what you're largely going after here with a more detailed internal way to get the port.
The reason why your code wasn't compiling was because you were inconsistent in what you were return
ing from the computation:
- In the case of
Some(port)
you were missing areturn
keyword - which is required to lift the value back into anAsync<port>
- Your
if
expression where you raise an exception had anelse
branch but you weren'treturn
ing from both. In this case, since you clearly don't wish to return anything and just raise an exception, you can omit theelse
and make it an imperative program flow just like in non-async code.
The other thing you may wish to consider down the road is if throwing an exception is what you want, or if just returning a Result<T,Err>
or an option is the right call. Exceptions aren't inherently bad, but often a lot of F# programming leads to avoiding their use if there's a good way to ascribe meaning to a type that wraps your return value.