关于使用 Hopac 创建 Alt
About Creating an Alt with Hopac
当我使用 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>
时有什么问题?
Hopac Alt 非常棘手,我花了一段时间才弄对。
当您return将Alt
转换为prepareFun
/prepareJob
时,您会想要return一个没有的Alt一直致力于。在 pf
的示例中,您 returning Alt.always
这意味着此 Alt
始终致力于。因此,当调用 na33 <|> na44 |> run
时,这意味着 na33
已经提交并且不需要 运行 na44
.
相比之下,delayedPrintn3
的示例使用的是 Async
,如果您查看 https://github.com/Hopac/Hopac/blob/master/Docs/Alternatives.md
中的参考实现
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
(think of them as the same as TaskCompletionSource),稍后将在异步 op
启动后设置。因此,在您的示例中,您可以看到两者都已启动,因为它们的 IVar
尚未提交。
如果您正在寻找类似的实现,例如:
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
其中 return 一个 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!!
当我使用 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>
时有什么问题?
Hopac Alt 非常棘手,我花了一段时间才弄对。
当您return将Alt
转换为prepareFun
/prepareJob
时,您会想要return一个没有的Alt一直致力于。在 pf
的示例中,您 returning Alt.always
这意味着此 Alt
始终致力于。因此,当调用 na33 <|> na44 |> run
时,这意味着 na33
已经提交并且不需要 运行 na44
.
相比之下,delayedPrintn3
的示例使用的是 Async
,如果您查看 https://github.com/Hopac/Hopac/blob/master/Docs/Alternatives.md
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
(think of them as the same as TaskCompletionSource),稍后将在异步 op
启动后设置。因此,在您的示例中,您可以看到两者都已启动,因为它们的 IVar
尚未提交。
如果您正在寻找类似的实现,例如:
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
其中 return 一个 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!!