Home > Enterprise >  Type mismatch in async method
Type mismatch in async method

Time:10-07

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 returning from the computation:

  • In the case of Some(port) you were missing a return keyword - which is required to lift the value back into an Async<port>
  • Your if expression where you raise an exception had an else branch but you weren't returning from both. In this case, since you clearly don't wish to return anything and just raise an exception, you can omit the else 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.

  • Related