在计算表达式中运行方法

3
在计算方法中,Run()方法的状态是什么?我在几个例子中看到过它(这里这里这里),我也在F#编译器源代码中看到过它,但它没有出现在规范MSDN文档中。我在MS Connect上提交了一个问题,但被标记为“按设计”,没有进一步的解释。
那么它是不推荐使用的/未记录的/不支持的吗?我应该避免使用它吗? 更新:MS Connect问题的状态很快就改变了,并更新了MSDN页面以包括Run()。

就我的博客而言,我是F#的相对新手。我使用像Run()这样的高级概念可能不一定代表最佳实践。当我刚学习它们时,我倾向于探索语言结构的奇怪和非典型用法,哈哈。 - TechNeilogy
2个回答

8

6.3.10 计算表达式

更具体地说,计算表达式的形式为builder-expr { cexpr },其中cexpr在语法上是表达式的语法结构,并定义了comp-expr中的附加构造。计算表达式用于序列和其他F#表达式语法的非标准解释。表达式builder-expr { cexpr }的翻译如下:

let b = builder-expr in b.Run (b.Delay(fun () -> {| cexpr |}C)) 

为一个新的变量b。如果在检查此表达式时,b的推断类型上不存在方法Run,则该调用将被省略。同样地,如果在检查此表达式时,b的类型上不存在方法Delay,则该调用也将被省略。

4
我认为Run方法是在开发过程中添加的,所以它可能没有出现在文档中的原因。正如desco所解释的那样,该方法用于“运行”计算表达式。这意味着无论何时编写expr { ... },翻译后的代码将被包装在对Run的调用中。
该方法有点问题,因为它会破坏组合性。例如,对于任何计算表达式,要求以下两个示例表示相同的内容是明智的:
expr { let! a = foo()           expr { let! c = expr {  
       let! b = bar(a)                   let! a = foo()
       let! c = woo(b)                   let! b = bar(a) 
       return! zoo(c) }                  return! woo(b) }
                                       return! zoo(c) }

然而,在左侧示例中,Run方法仅在整体结果上调用一次,在右侧则调用两次(对于整体计算表达式和嵌套的计算表达式)。该方法通常的类型签名为M<T> -> T,这意味着右侧代码甚至无法编译。
因此,在创建单子(如它们通常在Haskell中定义和使用)时,避免使用它是一个好主意,因为Run方法破坏了单子的某些良好方面。但是,如果你知道你在做什么,那么它可以很有用...
例如,在我的break代码中,计算生成器立即执行其主体(在声明中),因此添加Run以取消包装结果不会破坏组合性 - 组合只是运行另一个代码。然而,为async和其他延迟计算定义Run根本不是一个好主意。

哦,这让我有点担心...实际上我是在使用它来启用组合性(代码:http://github.com/mausch/FsSql/blob/938b55f7e057e95d10de2cca6f4f2679c870cc2e/FsSql/Transactions.fs#L120) - Mauricio Scheffer
@Mauricio:我不完全理解你的代码,但一个重要的区别是你的Run有一个类型为M<T> -> M<T> - 通常会破坏兼容性的情况是像M<T> -> T这样的(例如,当计算生成器创建任务并启动它时)。 - Tomas Petricek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接