lobsterism
lobsterism

Reputation: 3509

Should parameterless async workflows in F# be wrapped in functions?

If I've got an async parameterless workflow in F#, is it necessary and/or idiomatic to make that a function, or is it best left as a raw value?

For example if I want to define getRemoteCounterAfterOneSec, polls some remote counter source, should it be

let getRemoteCounterAfterOneSec = 
  async {
    do! Async.Sleep 1000
    return! ...
  }

or

let getRemoteCounterAfterOneSec () = 
  async {
    do! Async.Sleep 1000
    return! ...
  }

It seems like they should do the same thing, just the latter has an unnecessary parameter. However I've seen this done both ways in various code. I've also seen places where the behavior ends up different: if using a MailboxProcessor and doing

let myFunc = mailboxProc.PostAndAsyncReply(fun reply -> GetCount reply)
async {
  let! count1 = myFunc
  let! count2 = myFunc
}

then the mailboxProcessor is only called once; the second time it merely returns the same value calculated in the previous call. However if myFunc is a function then it calls mailboxProcessor twice as you'd expect.

let myFunc() = mailboxProc.PostAndAsyncReply(fun reply -> GetCount reply)
async {
  let! count1 = myFunc()
  let! count2 = myFunc()
}

Is that a bug in the implementation? What's going on here and what is idiomatic?

Upvotes: 4

Views: 94

Answers (1)

Tomas Petricek
Tomas Petricek

Reputation: 243051

When it comes to ordinary asyncs that you define yourself, then adding () has no effect (well, it means that the async that describes a computation is constructed repeatedly, but it has no practical effect).

I sometimes write it just to make my code easier to understand, or when the async is recursive (because then you get a warning when you have a recursive value). So, the following is fine, but it gives you a warning:

let rec loop = 
  async {
    do! Async.Sleep 1000
    if 1 > 2 then return 1
    else return! loop }

The PostAndAsyncReply method is written a bit differently - and the name tries to reflect that. Normal F# async methods are named AsyncFooBar. This one has PostAndAsyncFooBar to indicate that it first posts and then asynchronously waits, that is something like:

let PostAndAsyncWait () =
  post(message)
  async { let! sth = wait ()
          return sth }

So, here it actually posts outside of the async - this lets you call the function even if you are (syntactically) outside of an async block. And the name tries to be very explicit about this.
(But I would personally prefer if it was all inside async i.e. AsyncPostAndWait).

Upvotes: 2

Related Questions