F# 如何在异步计算表达式中保留泛型类型?
为什么泛型类型被限制为unit,我如何编写它,使myFunc正确地被类型化为F# 如何在异步计算表达式中保留泛型类型?,f#,F#,为什么泛型类型被限制为unit,我如何编写它,使myFunc正确地被类型化为unit->Async)=//myFunc:unit->Async 异步的{ 尝试 做!异步。睡眠500 return!func()/“t在此处约束为单位 与_ex-> do!Async.Sleep 200 用“失败”表示失败 } 编辑:我希望这将是一个很好的最小复制,但这里是我真正想要做的: let retryUntilTimeout (func: unit -> Async<'t>) timeou
unit->Async)=//myFunc:unit->Async
异步的{
尝试
做!异步。睡眠500
return!func()/“t在此处约束为单位
与_ex->
do!Async.Sleep 200
用“失败”表示失败
}
编辑:我希望这将是一个很好的最小复制,但这里是我真正想要做的:
let retryUntilTimeout (func: unit -> Async<'t>) timeout =
async {
let sw = Stopwatch.StartNew()
while sw.ElapsedMilliseconds < timeout do
try
return! func ()
with _ex ->
printfn "failed"
do! Async.Sleep 200
raise (TimeoutException())
}
let retryUntilTimeout(func:unit->Async每个表达式都必须有一个确定的类型,包括try…with
表达式。在这种情况下,有一个确定的类型意味着try
和with
分支必须具有相同的类型
但是在你的代码中,try
分支返回't
,而with
分支返回单位
。但没关系:既然't
可以是任何东西,我们可以将它与单位
统一起来,现在整个try…with
表达式也返回单位
。问题解决了
要解决此问题,您需要让with
分支也返回't
。如何做,从哪里获得't
?恐怕我无法帮助您,您必须决定
let myFunc (func: unit -> Async<'t>) =
async {
try
do! Async.Sleep 500
return! func()
with _ex ->
do! Async.Sleep 200
return someOtherValueOfT
failwith "failed"
}
(请注意,现在有一个额外的return
关键字在with
中-这是因为没有return
时,异步块被假定为具有类型单元
,我们回到了相同的问题)
对于您的评论,看起来您实际上要做的是一个无休止的迭代,受超时限制,并且只要出现错误,您就希望继续迭代
然而,代码的问题是,它实际上无法按预期的方式工作,即使您以某种方式从带有
分支的返回值't
。这是因为return!
关键字不像在C#(正式称为“早期返回”)中那样“中断执行”),但只运行给定的func()
,并使其值成为当前块的结果
事实上,如果您完全删除带有
分支的,问题将继续存在:编译器仍将坚持使用func()的类型
是单元
。这是因为调用在while
循环中,而while
循环的主体不能返回值,否则该值会被有效地丢弃,因此肯定存在某种错误。但是丢弃单元
是可以的,因此编译器允许这样做
一个很好的方法是通过递归:
let retryUntilTimeout (func: unit -> Async<'t>) timeout =
let sw = Stopwatch.StartNew()
let rec loop () = async {
if sw.ElapsedMilliseconds > timeout
then
return raise (TimeoutException())
else
try
return! func ()
with _ex ->
printfn "failed"
do! Async.Sleep 200
return! loop ()
}
loop ()
让retryUntilTimeout(func:unit->async)感谢您的详细回答。我恐怕这两个都不适用于我的用例,我在OP中没有描述它。我已经添加了我实际上正在尝试编写的函数;如果您也能就如何编写它给出建议,我将不胜感激。更新了答案
let myFunc (func: unit -> Async<'t>) =
async {
try
do! Async.Sleep 500
return! func()
with _ex ->
do! Async.Sleep 200
return failwith "failed"
}
let retryUntilTimeout (func: unit -> Async<'t>) timeout =
let sw = Stopwatch.StartNew()
let rec loop () = async {
if sw.ElapsedMilliseconds > timeout
then
return raise (TimeoutException())
else
try
return! func ()
with _ex ->
printfn "failed"
do! Async.Sleep 200
return! loop ()
}
loop ()