首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >重试monad与零构造

重试monad与零构造
EN

Stack Overflow用户
提问于 2014-04-17 09:44:27
回答 3查看 351关注 0票数 3

我正在尝试使用从我们心爱的堆栈溢出中获取的重试Monad:

代码语言:javascript
运行
复制
type RetryBuilder(max, sleep : TimeSpan) = 
      member x.Return(a) = a
      member x.Delay(f) = f
      member x.Zero() = failwith "Zero"
      member x.Run(f) =
        let rec loop(n) = 
            if n = 0 then failwith "Failed"
            else 
                try f() 
                with ex -> 
                    sprintf "Call failed with %s. Retrying." ex.Message |> printfn "%s"
                    Thread.Sleep(sleep); 
                    loop(n-1)
        loop max

我想使用它使我的文件副本代码更健壮一些:

代码语言:javascript
运行
复制
let retry = RetryBuilder(3, TimeSpan.FromSeconds(1.))
retry {
    System.IO.File.Move("a", "b")
}

现在,我注意到,有时在出现"Zero“异常时会失败。我试图删除member x.Zero() = failwith "Zero",但现在我得到了一个编译时错误:

只有当构建器定义了“Zero”方法时,才能使用此构造。

有什么办法吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2014-04-17 10:49:38

看起来最简单的解决方法是在最后返回一个值:

代码语言:javascript
运行
复制
retry {
    System.IO.File.Move("a", "b")
    return ()
}

如果您查看计算表达式是如何计算的?,您的代码似乎被转换为

代码语言:javascript
运行
复制
retry.Run(retry.Delay(fun () -> System.IO.File.Move("a", "b"); retry.Zero()))

这将导致计算期间抛出异常。如果您返回一个值,则不会发生这种情况。

票数 3
EN

Stack Overflow用户

发布于 2014-04-17 12:02:10

Lee建议您可以在计算结束时使用return (),否则会抛出,因为它们调用Zero成员。这是一个很好的技巧,但实际上您可以将其直接集成到计算生成器中。

当计算结束而不返回时,将使用Zero成员。您可以更改它以执行与return ()相同的操作。

代码语言:javascript
运行
复制
type RetryBuilder(max, sleep : TimeSpan) = 
  member x.Return(a) = ...
  member x.Zero() = x.Return( () )

然后只需编写原始代码,就可以得到单元结果:

代码语言:javascript
运行
复制
let retry = RetryBuilder(3, TimeSpan.FromSeconds(1.))
retry {
  System.IO.File.Move("a", "b")
}
票数 9
EN

Stack Overflow用户

发布于 2014-04-17 11:12:09

首先,需要对x.Run函数进行类型注释,以使编译器高兴,因为File.Move接受单元并返回单元。就像这样:

代码语言:javascript
运行
复制
open System
open System.Threading
type RetryBuilder(max, sleep : TimeSpan) = 
      member x.Return(a) = a
      member x.Delay(f) = f
      member x.Zero() = failwith "Zero"
      member x.Run(f : unit -> unit) =
        let rec loop(n) = 
            if n = 0 then failwith "Failed"
            else 
                try 
                    f() 
                with ex -> 
                    sprintf "Call failed with %s. Retrying." ex.Message |> printfn "%s"
                    Thread.Sleep(sleep); 
                    loop(n-1)
        loop max

然后查看Zero()函数上的文档,我们看到“在计算表达式中调用if...then表达式的空of分支”。因此,这就解释了为什么编译器需要在计算表达式中显示Zero。然后,如果我们在' if‘上设置一个断点,然后看着它执行,我们就会看到文件移动返回单元,这样其他人就没有什么可返回的了,因此它调用Zero。因此,这解释了为什么在移动成功时会弹出Zero (然后当它失败时重试,因为文件已经被移动并且不再存在)。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23129731

复制
相关文章

相似问题

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