Каждое выражение должно иметь определенный тип, включая выражение try ... with
. В данном случае наличие определенного типа означает, что ветви try
и with
должны иметь один и тот же тип.
Но в вашем коде ветвь try
возвращает 't
, а ветвь with
возвращает unit
. Но неважно: поскольку 't
может быть чем угодно, мы можем просто объединить его с unit
, и теперь все выражение try ... with
также возвращает unit
. Проблема решена!
Чтобы исправить это, вам нужно, чтобы ветвь 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"
}
Но, судя по общей форме кода, я подозреваю , что на самом деле вы имели в виду поместить failwith
в ветку with
. Поскольку failwith
может иметь любой тип, который вы хотите, компилятор будет доволен сохранением типа всего блока try ... with
как 't
:
let myFunc (func: unit -> Async<'t>) =
async {
try
do! Async.Sleep 500
return! func()
with _ex ->
do! Async.Sleep 200
return failwith "failed"
}
(обратите внимание, что теперь есть дополнительный return
ключевое слово внутри with
- это потому, что без return
предполагается, что блок asyn c имеет тип unit
, и мы возвращаемся к той же проблеме)
Отвечая на ваши комментарии, похоже, что вы на самом деле пытаетесь сделать бесконечную итерацию, ограниченную таймаутом, и вы хотите продолжать итерацию, пока есть ошибка.
Проблема с вашим кодом, однако, заключается в том, что он на самом деле не будет работать так, как вы ожидаете, даже если вы каким-то образом вернете значение 't
из ветки with
. Это связано с тем, что ключевое слово return!
не «прерывает выполнение», как в C# (формально известное как «ранний возврат»), а просто запускает заданный func()
и делает его значение результатом текущего блока. .
Фактически, если вы полностью удалите ветвь with
, проблема не исчезнет: компилятор по-прежнему будет настаивать на том, чтобы тип func()
был unit
. Это связано с тем, что вызов находится внутри while
l oop, а тело while
l oop не должно возвращать значение, иначе это значение будет отброшено, поэтому должна быть ошибка некоторых Сортировать. Но можно выбросить unit
, чтобы компилятор это разрешил.
Хороший способ сделать то, что вы пытаетесь сделать, - использовать рекурсию:
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 ()
Здесь, функция loop
сначала проверяет тайм-аут и выбрасывает (обратите внимание также на ключевое слово return
- это для удовлетворения системы типов), в противном случае запускает func()
, но если это не удается, немного ждет и вызывает себя рекурсивно, тем самым продолжая
Эта общая схема (или все, что на ней построено) - это то, как моделируются все итерации в функциональном программировании. Забудьте о петлях, они бесполезны.