用let!内部匹配语句导致编译错误

时间:2014-01-03 21:30:27

标签: f#

使用let有一些限制!里面的比赛声明?我不确定为什么这不会编译。

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

      return result
    }

编译失败,“此构造只能在计算表达式中使用”

3 个答案:

答案 0 :(得分:10)

如前所述,问题在于异步工作流只允许某些类型的嵌套 - 您不能在普通表达式中使用let!,而只能在计算表达式中使用。您的示例中的问题实际上不是match,而是let(包含match)。为了更好地了解发生了什么,规范(粗略地)看起来如下:

  在 cexpr
  | x = expr cexpr
  | 返回! expr
  | (...)

关键是参数只是一个普通的表达式expr,而letlet!之后的主体是另一个可以包含更多异步操作的计算表达式。

所以,如果你有let x = e1 in e2,那么let!只能e2e1不能{。}}。

在实践中,您可以执行Daniel建议并使用嵌套异步工作流,或者您可以重写代码,以便需要异步的代码不会使用等待内部表达式,这是不可能的 - 很难说一般来说如何做到这一点,但在你的具体例子中,你可以写:

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

答案 1 :(得分:6)

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
    }

答案 2 :(得分:3)

这很棘手,因为F#中的异步只是类似于c#中的异步。在F#中,它不是关键字,而是Computation Expression的实例。 bang(!)运算符[let !; do !; etc!]仅适用于计算表达式块。也就是说,它们仅适用于大括号下的代码。当您进行匹配时,您已经有效地创建了一个匿名方法,该方法对计算表达式一无所知。

就像c#的await关键字一样,let!运算符将运算符之后的代码从运算符之后的代码分离到单独的方法中。我怀疑代码之前的让!操作员必须能够从堆栈中完全放松,这是你的问题。需要能够解析“let result =”表达式,如果我们有“let!”则不能解析在它的中间。

我希望这会有所帮助。