引用计算表达式中的F#惰性
在计算表达式上使用Quote成员将工作流转换为AST,但希望在构造Quote时不会在序列上实际调用引用计算表达式中的F#惰性,f#,f#-3.0,computation-expression,F#,F# 3.0,Computation Expression,在计算表达式上使用Quote成员将工作流转换为AST,但希望在构造Quote时不会在序列上实际调用GetEnumerator()(即,具有某种形式的惰性)。在我的用例中,该序列表示一个远程数据源,调用该序列上的GetEnumerator()成员将实际执行查询 是否有某种方法可以隐式地在上使用Lazy类型(并且仍然使用Quote成员) 源成员,使其不急于调用GetEnumerator(),而是 只是还没有加载值 为什么let绑定定义为一个模块的属性和另一个模块中的变量 在报价单中被视为不同实体的函
GetEnumerator()
(即,具有某种形式的惰性)。在我的用例中,该序列表示一个远程数据源,调用该序列上的GetEnumerator()
成员将实际执行查询
GetEnumerator()
,而是
只是还没有加载值PropertyGet
vs
值
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()
模块示例
打开Microsoft.FSharp.quotes
[]
第一类
类型FooBuilder()=
成员来源(x:#seq=invalidOp“未实现”
成员_uu.For(来源:I I=invalidOp“未实施”
成员_uu.Zero():I>)=expr
成员运行(expr:expr
是否有某种方法可以隐式地在源成员上使用Lazy类型(并且仍然使用Quote成员),这样它就不会急切地调用GetEnumerator(),而只是还没有加载值
我不认为有办法隐式使用Lazy类型。但是,我不太理解这个问题-当您使用Quote
方法时,您可以对得到的引号执行任何转换,这样您就可以转换引号,使其不会实际调用GetEnumerator
成员(当然,您必须用其他返回数据的东西替换它…)
关键的一点是,构建查询不会调用GetEnumerator
方法。因此,您应该能够在不调用GetEnumerator
的情况下获取报价并对其进行转换和评估
为什么let绑定被定义为一个模块的属性和另一个函数中的变量,在引号中被视为不同的实体,即PropertyGet
vsValue
模块中的let绑定被编译成静态成员,因此引号捕获对此静态成员的引用。对于局部变量,无法捕获引用,因此Value
节点将数据直接嵌入引号中。(您可以编写一个转换,通过实际从属性获取当前值,将PropertyGet
转换为Value
)
EDIT:当我创建一个调用GetEnumerator
时抛出的IEnumerable
时,F#interactive中打印的引号显示错误(因为F#interactive试图计算序列以输出前几个成员),但引号仅包含源代码作为值
如果从构建器中删除Run
方法(以便它只返回引号),那么这应该可以工作(并返回“fancy source”字符串):
type FancySource()=
成员x.Source=“花式来源”
接口seq与
成员x.GetEnumerator()=故障带“!”
接口System.Collections.IEnumerable与
成员x.GetEnumerator()=故障带“!”
让insideLet()=
let bar=new FancySource()
foo{for x in bar do()}
将insideLet()与
|Patterns.Call(,,Patterns.Call(,,[Patterns.Value(:?FancySource as v,)]):::->
v、 来源
在对源代码的调用中,参数是一个包含值的单例列表。在我的示例中,它是列表[1;2;3],但在我的远程数据源中,它是结果的序列。因此GetEnumerator()
以某种方式被内部调用以生成它。@user2712561我认为它实际上并没有调用GetEnumerator
。请参见编辑。
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,
[]))))])
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