Нужно ли использовать еще ветку в асинхронных выражениях?

Я хочу написать следующий код:

let someAsync () = async {
    if 1 > 2 then return true // Error "this expression is expected to have type unit ..."
    // I want to place much code here    
    return false
}

F# почему-то считает, что мне нужно написать так:

let someAsync () = async {
    if 1 > 2 then return true
    else
        // Much code here (indented!)
        return false
}

В последнем случае сообщение об ошибке не выдается. Но, на мой взгляд, обе части кода эквивалентны. Есть ли шанс, что я смогу избежать ненужных вложений и отступов?

UPD. То, что я спрашиваю, действительно возможно! Пожалуйста, посмотрите на пример, смотрите раздел Пример реального мира.

Я процитирую код:

let validateName(arg:string) = imperative {
    if (arg = null) then return false  // <- HERE IT IS
    let idx = arg.IndexOf(" ")
    if (idx = -1) then return false    // <- HERE IT IS

    // ......
    return true 
}

Так что, возможно, вопрос только в том, можно ли как-то реализовать в asyncчерез расширение модуля или что-то еще.

2 ответа

Решение

Существует важное различие между async построитель вычислений и мой imperative строитель.

В asyncВы не можете создать полезное вычисление, которое не возвращает значение. Это означает, что Async<'T> представляет вычисление, которое в конечном итоге приведет к значению типа 'T, В этом случае async.Zero метод должен вернуть unit и имеет подпись:

async.Zero : unit -> Async<unit>

За imperiatve строитель, тип Imperative<'T> представляет вычисление, которое может возвращать или не возвращать значение. Если вы посмотрите на объявление типа, оно выглядит следующим образом:

type Imperative<'T> = unit -> option<'T>

Это означает, что Zero операция (которая используется, когда вы пишете if без else) может быть вычислением любого типа. Так, imperative.Zero Метод возвращает вычисление любого типа:

imperative.Zero : unit -> Imperative<'T>

Это принципиальная разница, которая также объясняет, почему вы можете создать if без else ветвь (потому что Zero метод может создавать вычисления любого типа). Это невозможно для async, так как Zero можно только создать unitВозврат ценностей.

Таким образом, два вычисления имеют разные структуры. В частности, "императивные" вычисления имеют моноидальную структуру, а асинхронные рабочие процессы - нет. Более подробно вы можете найти объяснение в нашей статье F# Computation Zoo

Я думаю, что ситуация описана здесь: Условные выражения: if... then... else (F#)

(...) если тип then ветвь любого типа, кроме unitдолжно быть else ветвь с тем же типом возврата.

Ваш первый код не имеет else ветка, которая вызвала ошибку.

Другие вопросы по тегам