Использование let! внутри операторов совпадения вызывает ошибку компиляции

Есть ли какое-то ограничение на использование let! внутри матчей? Я не уверен, почему это не будет компилироваться.

module Foo =
  let Bar =
    async {      
      let result =
        match 1 with
        | 1 ->
          let! num = async.Return 12345 // Doesn't compile
          1
        | _ -> 2

      return result
    }

Сбой компиляции с помощью "Эта конструкция может использоваться только в выражениях вычислений"

Ответ 1

Как уже объяснялось, проблема в том, что асинхронные рабочие процессы допускают только определенные типы вложений - вы не можете использовать let! внутри обычного выражения, но только внутри выражения вычисления. Проблема в вашем примере на самом деле не является match, а let (которая содержит match). Чтобы лучше понять, что происходит, спецификация выглядит (примерно) следующим образом:

cexpr: = let! x = expr в cexpr
| let x = expr в cexpr
| return! expr
| (...)

Главное, что аргумент является просто обычным выражением expr, а тело, следующее за let или let!, является другим выражением вычисления, которое может содержать больше асинхронных операций.

Итак, если у вас есть let x = e1 in e2, вы можете иметь let! только в e2, но не в e1.

На практике вы можете делать то, что предлагает Дэниел, и использовать вложенный асинхронный рабочий процесс, или вы можете переписать свой код так, чтобы код, который должен быть асинхронным, не использовал ожидания внутри выражений там, где это невозможно - трудно сказать как это сделать вообще, но в вашем конкретном примере вы можете просто написать:

let bar = async {      
  match 1 with
  | 1 ->
      let! num = async.Return 12345 
      return 1
  | _ -> 
      return 2 }

Ответ 2

Вызовы Bind (через let!) должны отображаться на верхнем уровне выражения вычисления. Вы можете исправить это, создав вложенный блок async { }:

module Foo =
  let Bar =
    async {      
      let result = async {
        match 1 with
        | 1 ->
          let! num = async.Return 12345
          return 1
        | _ -> return 2
      }
      return result
    }

Ответ 3

Это сложно, потому что async в F # только вроде async в С#. В F # это не ключевое слово, это экземпляр Вычисление выражения. Операторы bang (!) [Let!; Do!, и т.д.] Работают только в блоках вычислений. То есть, они работают только для кода сразу под фигурными фигурными скобками. Когда вы выполняете совпадение, как вы это сделали, вы создали анонимный метод, который ничего не знает о выражении вычисления.

Как и ключевое слово С# wait, пусть! оператор отделяет код перед оператором от кода после оператора в отдельные методы. Я подозреваю, что код перед выпуском! оператор должен быть в состоянии полностью расслабиться от стека, и это ваша проблема здесь. Выражение "let result =" должно быть разрешено и не может, если у нас есть "let!" в середине его.

Я надеюсь, что это поможет.