2

Is there any primitive in the langage to compose async1 then async2, akin to what parallel does for parallel execution planning ?

to further clarify, I have 2 async computations

  let toto1 = Async.Sleep(1000)
  let toto2 = Async.Sleep(1000)

I would like to create a new async computation made of the sequential composition of toto1 and toto2

  let toto = Async.Sequential [|toto1; toto2|]

upon start, toto would run toto1 then toto2, and would end after 2000 time units

Brian Rasmussen
  • 114,645
  • 34
  • 221
  • 317
nicolas
  • 9,549
  • 3
  • 39
  • 83
  • What is there to "try" here ? yeah I "tried" answering my question reading the API on msdn.. – nicolas Mar 08 '12 at 16:26
  • Is [`Async.StartChild`](http://msdn.microsoft.com/en-us/library/ee370511.aspx) what you want? – Daniel Mar 08 '12 at 16:26
  • I saw this, but it looks like StartChild forks a new parallel child upon execution. – nicolas Mar 08 '12 at 16:32
  • 1
    @nicolas You didn't even indicate that you'd read anything. By telling us what you've looked at or showing code that you've tried to solve this you can prevent us from duplicating or responding as if you've just decided to ask rather than do anything. – Lazarus Mar 08 '12 at 16:32
  • @Lazarus I am asking about a primitive of the langage. there is nothing to "try". – nicolas Mar 08 '12 at 16:34
  • 1
    @nicolas I would call looking at the documentation "trying" to find an answer, wouldn't you? That said `Async` isn't a primitive, it's part of the .Net Framework. You're going to need to move up into the framework and write some code. Couldn't you use a continuation that spawns async2? – Lazarus Mar 08 '12 at 16:43
  • @Lazarus I would call that 'common sense' – nicolas Mar 08 '12 at 16:50
  • @nicolas Look through more of the other questions... you'll find common sense really isn't all that common ;) – Lazarus Mar 08 '12 at 16:55
  • 1
    What's `toto` in your example? Is it the result of `toto1` or `toto2`? What do you want to do with the other result? Ignore it? Do you want to combine them? If so, how? – Mauricio Scheffer Mar 08 '12 at 17:03

2 Answers2

4

The async.Bind operation is the basic primitive that asynchronous workflows provide for sequential composition - in the async block syntax, that corresponds to let!. You can use that to express sequential composition of two computations (as demonstrated by Daniel).

However, if you have an operation <|> that Daniel defined, than that's not expressive enough to implement async.Bind, because when you compose things sequentially using async.Bind, the second computation may depend on the result of the first one. The <e2> may use v1:

async.Bind(<e1>, fun v1 -> <e2>)

If you were writing <e1> <|> <e2> then the two operations have to be independent. This is a reason why the libraries are based on Bind - because it is more expressive form of sequential composition than the one you would get if you were following the structure of Async.Parallel.

If you want something that behaves like Async.Parallel and takes an array, then the easiest option is to implement that imperatively using let! in a loop (but you could use recursion and lists too):

let Sequential (ops:Async<'T>[]) = async {
  let res = Array.zeroCreate ops.Length
  for i in 0 .. ops.Length - 1 do
    let! value = ops.[i]
    res.[i] <- value 
  return res }
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • Out of curiosity, would this function any differently than `async1 <|> async2 <|> ... asyncN` using the combinator in my answer? – Daniel Mar 08 '12 at 21:24
  • 1
    Of interest is that it is **not** Async.Bind, but **async.Bind** I had missed entirely this, and was looking at the control.Async module in MSDN. (Might be good to add this to Async for regularity) – nicolas Mar 09 '12 at 08:06
  • @nicolas Ah, yes, sorry for the confusion :-) The `Bind` operation is poart of the async computation builder (the `async` value) which is used in the translation of `async { .. }` block. Additional combinators are exposed in a module (as static members). – Tomas Petricek Mar 10 '12 at 20:25
2

I'm not sure what you mean by "primitive." Async.Parallel is a function. Here are a few ways to run two asyncs:

In parallel:

Async.Parallel([|async1; async2|])

or

async {
  let! child = Async.StartChild async2
  let! result1 = child
  let! result2 = async1
  return [|result1; result2|]
}

Sequentially:

async {
  let! result1 = async1
  let! result2 = async2
  return [|result1; result2|]
}

You could return tuples in the last two. I kept the return types the same as the first one.

I would say let! and do! in an async { } block is as close as you'll get to using a primitive for this.

EDIT

If all this nasty syntax is getting to you, you could define a combinator:

let (<|>) async1 async2 = 
  async {
    let! r1 = async1
    let! r2 = async2
    return r1, r2
  }

and then do:

async1 <|> async2 |> Async.RunSynchronously
Daniel
  • 47,404
  • 11
  • 101
  • 179
  • by primitive, I mean a function that is not made of a combination simpler functions, aka something in the langage. – nicolas Mar 08 '12 at 16:45
  • 1
    `let!` and `do!` are syntactic sugar for continuations, which is what you want. Are they not "primitive" enough? – Daniel Mar 08 '12 at 16:46
  • thank you Daniel, I figured out early I could write a combinator, but just like there is one for // computations, I was just wondering if another was hidden somewhere. – nicolas Mar 08 '12 at 16:54
  • May be they did not add it as it is not as straightforward if you want to handle the general case with composition, where types of the call chain have to be compatible. for unit, of course it is trivial. (actually for unlimited sequence it is probably not representable in the type system) – nicolas Mar 08 '12 at 17:03
  • Yeah, I don't think a general-purpose `bind` is possible. You can't know, for every `M<'T>`, which values of `'T` are failure/success. – Daniel Mar 08 '12 at 17:05