在计算表达式上使用Quote成员将工作流转换为AST,但是希望它在构造引号时不会在序列上实际调用GetEnumerator()
(即,有一些形式懒惰)。在我的用例中,序列表示一个远程数据源,并且调用其上的GetEnumerator()
成员实际上将会对其进行查询。
是否有某种方法可以隐式使用Lazy类型(并且仍然使用Quote成员)
Source成员,因此它不会急切地呼叫GetEnumerator()
而是
根本没有载入价值呢?
为什么let绑定被定义为模块的属性和另一个中的变量
功能被视为报价中的不同实体,即PropertyGet
vs
Value
。
一些测试代码......
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,
[]))))])
答案 0 :(得分:2)
有没有办法在Source成员上隐式使用Lazy类型(并且仍然使用Quote成员),这样它就不会急切地调用GetEnumerator()而只是没有加载该值呢?
我认为没有办法隐式使用Lazy类型。但是,我不太明白这个问题 - 当你使用Quote
方法时,你可以对你得到的引用进行任何转换,这样你就可以转换引号,使它实际上不会调用{{1成员(当然,你必须用其他返回数据的东西替换它......)
关键是构建查询不会调用GetEnumerator
方法。所以你应该能够得到报价和转变它&amp;在不调用GetEnumerator
的情况下对其进行评估。
为什么将let绑定定义为模块的属性,将另一个函数中的变量定义为引号中的不同实体,即
GetEnumerator
vsPropertyGet
。
模块中的let绑定被编译为静态成员,因此引号捕获对此静态成员的引用。对于局部变量,无法捕获引用,因此Value
节点将数据直接嵌入到引号中。 (您可以通过实际获取属性中的当前值来编写将Value
转换为PropertyGet
的转换。
编辑:当我在调用Value
时创建抛出的IEnumerable
时,F#interactive中的打印引号显示错误(因为F#interactive尝试评估序列输出前几个成员),但引用只包含源GetEnumerator
。
如果从构建器中删除Value
方法(以便它只返回引号),那么这应该有效(并返回“花式源”字符串):
Run