When I use Hopac to create Alt<unit>
with Alt.<functions>
like always or once it would lead me to strange negative acknowledgement outcome.
But if I use async {<expression>}
to create Alt<unit>
then everything works as expected.
open Hopac
open Hopac.Core
open Hopac.Infixes
open Hopac.Extensions
let pf m (s:int) = Alt.prepareFun <| fun _ ->
Alt.always () ^=> fun _ ->
job {
printfn "starting [%s] %d" m Thread.CurrentThread.ManagedThreadId
Thread.Sleep s
printfn "%s" m }
|> Job.start
let delayedPrintn3 msg delayInMillis =
Alt.prepareFun <| fun _ ->
async {
printfn "starting [%s] %d" msg Thread.CurrentThread.ManagedThreadId
do! Async.Sleep delayInMillis
}
|> Alt.fromAsync
|> Alt.afterFun (fun _ -> printfn "%s" msg)
let na : (string -> int -> Alt<unit>) -> string -> string -> int -> Alt<unit> = fun ff s1 s2 i ->
Alt.withNackJob <|
fun nack ->
nack
|> Alt.afterFun (fun () ->
printfn "%s" s1)
|> Job.start
|> Job.map (fun _ -> ff s2 i)
let na11 = na delayedPrintn3 "1 canceled!!" "na11" 3
let na22 = na delayedPrintn3 "2 canceled!!" "na22" 0
let na33 = na pf "1 canceled!!" "na33" 3
let na44 = na pf "2 canceled!!" "na44" 0
na22 <|> na11 |> run
na33 <|> na44 |> run
And the result are:
starting [na22] 18
starting [na11] 18
na22
1 canceled!!
and
starting [na33] 11
na33
However I want to get the same result. What's the problem when using Alt.<function>
?
Hopac Alt's are extremely tricky and it took me a while to get them right.
When you are returning
Alt
toprepareFun
/prepareJob
, you're going to want to return an Alt that hasn't been committed to. In your sample forpf
you're returningAlt.always
which means thisAlt
is committed to always. So when callingna33 <|> na44 |> run
this meansna33
has already been committed to and doesn't need to runna44
.In contrast, example of
delayedPrintn3
is usingAsync
and if you look at the reference implementation from https://github.com/Hopac/Hopac/blob/master/Docs/Alternatives.mdit's creating an
IVar
(think of them as the same as TaskCompletionSource) which later will be set after the Asyncop
is started. So in your example you can see both being started since theirIVar
s haven't been committed to yet.If you're looking for a similar implementation, something like:
Which returns an
IVar
(which is anAlt
) that hasn't been committed to. I had to up the sleep time to 100 to make sure Hopac didn't commit to the first one too quickly.