引用计算表达式中的F#laziness

时间:2013-08-23 23:05:20

标签: f# f#-3.0 computation-expression

在计算表达式上使用Quote成员将工作流转换为AST,但是希望它在构造引号时不会在序列上实际调用GetEnumerator()(即,有一些形式懒惰)。在我的用例中,序列表示一个远程数据源,并且调用其上的GetEnumerator()成员实际上将会对其进行查询。

  1. 是否有某种方法可以隐式使用Lazy类型(并且仍然使用Quote成员)  Source成员,因此它不会急切地呼叫GetEnumerator()而是  根本没有载入价值呢?

  2. 为什么let绑定被定义为模块的属性和另一个中的变量  功能被视为报价中的不同实体,即PropertyGet vs  Value

  3. 一些测试代码......

    module Example
    
        open Microsoft.FSharp.Quotations
    
        [<Interface>]
        type I<'Type> =
            inherit seq<'Type>
    
        type FooBuilder() =
    
            member __.Source (x : #seq<'Type>) : I<'Type> = invalidOp "not implemented"
    
            member __.For (source : I<'Type>, f : 'Type -> I<'Type>) : I<'Type> = invalidOp "not implemented"
    
            member __.Zero () : I<'Type> = invalidOp "not implemented"
    
            member __.Quote (expr : Expr<#seq<'Type>>) = expr
    
            member __.Run (expr : Expr<#seq<'Type>>) =
                System.Console.WriteLine(expr)
    
        let foo = FooBuilder()
    
        let bar = [1; 2; 3]
        foo {
            for x in bar do ()
        }
    
        let insideLet() =
            let bar = [1; 2; 3]
            foo {
                for x in bar do ()
            }
    
        insideLet()
    

    导致以下两个引用

    Call (Some (Value (FSI_0009+FooBuilder)), For,
          [Call (Some (Value (FSI_0009+FooBuilder)), Source,
                 [PropertyGet (None, bar, [])]),
           Lambda (_arg1,
                   Let (x, _arg1,
                        Sequential (Value (<null>),
                                    Call (Some (Value (FSI_0009+FooBuilder)), Zero,
                                          []))))])
    
    Call (Some (Value (FSI_0009+FooBuilder)), For,
          [Call (Some (Value (FSI_0009+FooBuilder)), Source, [Value ([1; 2; 3])]),
           Lambda (_arg1,
                   Let (x, _arg1,
                        Sequential (Value (<null>),
                                    Call (Some (Value (FSI_0009+FooBuilder)), Zero,
                                          []))))])
    

1 个答案:

答案 0 :(得分:2)

  

有没有办法在Source成员上隐式使用Lazy类型(并且仍然使用Quote成员),这样它就不会急切地调用GetEnumerator()而只是没有加载该值呢?

我认为没有办法隐式使用Lazy类型。但是,我不太明白这个问题 - 当你使用Quote方法时,你可以对你得到的引用进行任何转换,这样你就可以转换引号,使它实际上不会调用{{1成员(当然,你必须用其他返回数据的东西替换它......)

关键是构建查询不会调用GetEnumerator方法。所以你应该能够得到报价和转变它&amp;在不调用GetEnumerator的情况下对其进行评估。

  

为什么将let绑定定义为模块的属性,将另一个函数中的变量定义为引号中的不同实体,即GetEnumerator vs PropertyGet

模块中的let绑定被编译为静态成员,因此引号捕获对此静态成员的引用。对于局部变量,无法捕获引用,因此Value节点将数据直接嵌入到引号中。 (您可以通过实际获取属性中的当前值来编写将Value转换为PropertyGet的转换。

编辑:当我在调用Value时创建抛出的IEnumerable时,F#interactive中的打印引号显示错误(因为F#interactive尝试评估序列输出前几个成员),但引用只包含源GetEnumerator

如果从构建器中删除Value方法(以便它只返回引号),那么这应该有效(并返回“花式源”字符串):

Run