F# 引用 : variable may escape scope

标签 f# metaprogramming multi-stage-programming metaocaml

我有这段代码:

let rec h n z = if n = 0 then z
                else <@ (fun x -> %(h (n - 1) <@ x + %z @>)) n @>

http://www.cs.rice.edu/~taha/publications/journal/dspg04a.pdf 中的 MetaOcaml 示例转换而来

在论文中解释了上面的例子将产生以下参数3.<1>. (以 MetaOcaml 表示法):
.<(fun x_1 -> (fun x_2 -> (fun x_3 -> x_3 + (x_2 + (x_1 + 1))) 1) 2) 3>.

如您所见 x 's 被 x_1 取代, x_2等等,因为 x否则只会引用 x在最里面fun .

但在 F# 中这是不允许的。我收到编译时错误:“变量 'x' 绑定(bind)在引号中,但用作拼接表达式的一部分。这是不允许的,因为它可能会超出其范围。”所以问题是:如何改变它以便编译并具有与 MetaOcaml 输出相同的语义?

更新评论:我使用 PowerPack 来实际评估报价。但我认为这与它没有任何关系,因为错误发生在编译时。到目前为止 QuotationEvaluation 有效。但是,我知道这可能不是最有效的实现。

更新 Tomas 的回答:
我真的不想要 x成为全局性的,或逃避范围。但我想要的是相当于
let rec h n z = if n = 0 then z
                else (fun x -> (h (n - 1) (x + z))) n

带引号。你的回答给了(h 3 <@ 1 @>).Eval() = 4以上产生h 3 1 = 7 .在这里,我想要 7成为答案。

最佳答案

F# 引号语法不支持可能逃逸范围的变量,因此您需要使用 Expr 显式构造树。操作。这样的事情应该可以解决问题:

open Microsoft.FSharp.Quotations

let rec h n (z:Expr<int>) = 
  if n = 0 then z                
  else 
    let v = new Var("x", typeof<int>)
    let ve = Expr.Var(v)
    Expr.Cast<int>
        (Expr.Application( Expr.Lambda(v, h (n - 1) <@ %%ve + %z @>), 
                           Expr.Value(n)))

但是,这是一个非常人为的示例(用于演示 MetaOCaml 中的变量捕获,这在 F# 中不可用)。它只是生成像 (2 + (1 + ...)) 这样的表达式.您可以通过编写如下内容获得相同的结果:
let rec h n (z:Expr<int>) = 
  if n = 0 then z                
  else h (n - 1) <@ n + %z @>

甚至更好:
[ 1 .. 4 ] |> List.fold (fun st n -> <@ n + %st @>) <@ 0 @>

我在 F# 引用中也遇到了这个限制,如果支持它会很好。但是,我认为在实践中这不是一个大问题,因为 F# 引用不用于分阶段元编程。它们对于分析现有 F# 代码比生成代码更有用。

关于F# 引用 : variable may escape scope,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6414185/

相关文章:

c++ - 编译时专门针对函数指针引用以避免 -Waddress

f# - F# 引用的另一个限制?

.net - dotnet ef 迁移列表在迁移文件夹中找不到迁移

F# SignalR 动态类

f# - 如何使用类型提供程序编辑 XML 文件?

f# - float 数组、float[] 和 double[] 是不同的还是相同的?

ruby - 提取祖先到 "me"

C++模板非类型参数类型推导