1

当我使用 Hopac 像往常一样或曾经创建Alt<unit>时,Alt.<functions>它会导致我产生奇怪的否定确认结果。

但是,如果我async {<expression>}用来创建,Alt<unit>那么一切都会按预期进行。

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

结果是:

starting [na22] 18
starting [na11] 18
na22
1 canceled!!

starting [na33] 11
na33

但是我想得到相同的结果。使用时有什么问题Alt.<function>

4

1 回答 1

2

Hopac Alt 非常棘手,我花了一段时间才把它们弄好。

当您返回Alt/prepareFunprepareJob,您将希望返回尚未提交的 Alt。在您pf返回的样本中,Alt.always这意味着Alt始终致力于此。所以当调用na33 <|> na44 |> run这个意味着na33已经被提交并且不需要运行na44.

相反,如果您查看https://github.com/Hopac/Hopac/blob/master/Docs/Alternatives.mddelayedPrintn3中的参考实现,则使用的示例Async

open System.Threading

let asyncAsAlt (xA: Async<'x>) : Alt<'x> = Alt.withNackJob <| fun nack ->
  let rI = IVar ()
  let tokenSource = new CancellationTokenSource ()
  let dispose () =
    tokenSource.Dispose ()
    // printfn "Dispose"
  let op = async {
      try
        let! x = xA
        do rI *<= x |> start
        // do printfn "Success"
      with e ->
        do rI *<=! e |> start
        // do printfn "Failure"
    }
  Async.Start (op, cancellationToken = tokenSource.Token)
  nack
  >>- fun () ->
        tokenSource.Cancel ()
        // printfn "Cancel"
        dispose ()
  |> Job.start >>-.
  Alt.tryFinallyFun rI dispose

它正在创建一个IVar(认为它们与TaskCompletionSource相同),稍后将在 Asyncop启动后设置。因此,在您的示例中,您可以看到两者都已启动,因为它们IVar的 s 尚未提交。

如果您正在寻找类似的实现,例如:

let pf2 m (s:int) = Alt.prepareJob <| fun _ ->
    let retVal = IVar<unit>()
    job { 
        printfn "starting [%s] %d" m Thread.CurrentThread.ManagedThreadId
        do! timeOutMillis s
        printfn "%s" m 
        do! IVar.fill retVal ()
    }
    |> Job.start
    >>-. retVal

它返回一个尚未承诺的IVar(这是一个)。Alt我不得不将睡眠时间提高到 100 以确保 Hopac 不会太快地投入到第一个。


let na55 = na pf2 "1 canceled!!" "na55" 100
let na66 = na pf2 "2 canceled!!" "na66" 0

starting [na55] 9
starting [na66] 9
na66
1 canceled!!
于 2019-09-16T17:42:44.423 回答