这是一个F#引用错误吗?

con*_*low 5 reflection recursion f# quotations

[<ReflectedDefinition>]
let rec x = (fun() -> x + "abc") ()
Run Code Online (Sandbox Code Playgroud)

上面带有递归值的示例代码会产生以下F#编译器错误:

错误FS0432:[<ReflectedDefinition>]术语不能包含前缀拼接运算符'%'的使用

我在上面的代码中看不到任何切片操作符用法,看起来像一个bug ... :)

看起来这只是报价的问题ReflectedDefinitionAttribute,正常报价效果很好:

let quotation =
    <@ let rec x = (fun() -> x + "abc") () in x @>
Run Code Online (Sandbox Code Playgroud)

通过隐藏Lazy.createLazy.force使用产生预期结果:

val quotation : Quotations.Expr<string> =
   LetRecursive
   ([(x, Lambda (unitVar,
        Application
        (Lambda (unitVar0,
            Call (None,
            String op_Addition[String,String,String](String, String),
            [Call (None,
                String Force[String](Lazy`1[System.String]), // `
                [x]), Value ("abc")])),
        Value (<null>)))),
   (x, Call (None, Lazy`1[String] Create[String](FSharpFunc`2[Unit,String]), [x])),
   (x, Call (None, String Force[String](Lazy`1[String]), [x]))], x) // `
Run Code Online (Sandbox Code Playgroud)

所以问题是:这是一个F#编译器错误吗?

Tom*_*cek 5

我认为这可能是由F#中递归值的处理引起的.作为解决方法,您可以将递归引用转换为参数:

[<ReflectedDefinition>] 
let foo x = (fun() -> x + "abc") ()

// To construct the recursive value, you'd write:
let rec x = foo x
Run Code Online (Sandbox Code Playgroud)

最后一行当然是无效的(就像你的原始代码一样),因为你正在创建一个直接的递归引用,但是它应该给你一个想法 - 实际上,你可能x只是在一个lambda函数中.


编辑 最初,我认为问题可能如下,但我现在不确定(见评论).

对于我而言,它看起来更像是一个(可能是已知的)限制而不是意外的错误.您编写的代码的两个版本之间存在重要差异 - 在第一种情况下,您绑定了一个公共值(对.NET可见),x而在第二种情况下,x它只是一个仅在引用中使用的符号.

必须存储在程序集的元数据中的引用如下所示:

let rec x = <@ (fun() -> %x + "abc") () @>
Run Code Online (Sandbox Code Playgroud)

引用了正文,但x不是引号,因此需要将其拼接到引号中(即,将对其进行求值,并将结果用于其位置).请注意,此代码将失败,因为您正在声明具有立即引用的递归值 - x需要作为其定义的一部分进行评估,因此这将不起作用.

但是,我认为%不能出现在ReflectedDefinition引号中(也就是说,你不能将上述内容存储在元数据中),因为它涉及一些运行时方面 - 你需要x在加载元数据时进行评估.