F#引用计算表达式中的惰性



在计算表达式上使用Quote成员将工作流转换为AST,但希望在构造报价时不会在序列上实际调用GetEnumerator()(即具有某种形式的惰性)。在我的用例中,序列表示一个远程数据源,调用它上的GetEnumerator()成员实际上会出去查询它

  1. 是否有某种方法可以在上隐式使用Lazy类型(并且仍然使用Quote成员)源成员,以便它不急切地调用CCD_ 3,而是具有只是还没有加载值?

  2. 为什么let绑定被定义为模块的属性和另一个模块中的变量函数在报价中被视为不同的实体,即PropertyGetValue

一些测试代码。。。

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,
                                      []))))])

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

我不认为有任何方法可以隐含地使用Lazy类型。然而,我不太理解这个问题——当你使用Quote方法时,你可以对你得到的报价执行任何转换,所以你可以转换报价,这样它就不会真正调用GetEnumerator成员(当然,你必须用其他返回数据的东西来替换它…)

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

为什么let绑定被定义为另一个函数中的模块和变量的属性,在引号中被视为不同的实体,即PropertyGetValue

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

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

如果您从构建器中删除Run方法(这样它就只返回报价),那么这应该有效(并返回"花式源"字符串):

type FancySource() = 
  member x.Source = "fancy source"
  interface seq<int> with
    member x.GetEnumerator() = failwith "!" 
  interface System.Collections.IEnumerable with
    member x.GetEnumerator() = failwith "!" 
let insideLet() =
  let bar = new FancySource()
  foo { for x in bar do () }
match insideLet() with
| Patterns.Call(_, _, Patterns.Call(_, _, [Patterns.Value(:? FancySource as v, _)])::_) -> 
    v.Source

相关内容

  • 没有找到相关文章

最新更新