首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >关于使用Hopac创建Alt

关于使用Hopac创建Alt
EN

Stack Overflow用户
提问于 2019-09-16 20:02:40
回答 1查看 139关注 0票数 1

当我使用Hopac创建带有Alt.<functions>Alt<unit>时,就像总是或一次一样,它会导致奇怪的否定确认结果。

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

代码语言:javascript
复制
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

结果是:

代码语言:javascript
复制
starting [na22] 18
starting [na11] 18
na22
1 canceled!!

代码语言:javascript
复制
starting [na33] 11
na33

然而,我希望得到相同的结果。使用Alt.<function>时有什么问题

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-09-17 01:42:44

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

当您将Alt返回到prepareFun/prepareJob时,您将需要返回一个尚未提交的Alt。在您的pf示例中,您返回的是Alt.always,这意味着此Alt将始终被提交。因此,当调用na33 <|> na44 |> run时,这意味着na33已经被提交,不需要运行na44

相比之下,delayedPrintn3的示例使用了Async,如果您查看来自https://github.com/Hopac/Hopac/blob/master/Docs/Alternatives.md的参考实现

代码语言:javascript
复制
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相同),稍后将在启动异步op后设置它。所以在你的例子中,你可以看到它们都是启动的,因为它们的IVar还没有被提交。

如果您正在寻找类似的实现,则如下所示:

代码语言:javascript
复制
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不会太快地完成第一个任务。

代码语言:javascript
复制
let na55 = na pf2 "1 canceled!!" "na55" 100
let na66 = na pf2 "2 canceled!!" "na66" 0
代码语言:javascript
复制
starting [na55] 9
starting [na66] 9
na66
1 canceled!!
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57956469

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档