F# 如何在异步计算表达式中保留泛型类型?

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,我如何编写它,使myFunc正确地被类型化为
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 ()