F# 是否支持'call-by-name'语义?


一段时间以来,F#支持使用[<ReflectedDefinitionAttribute>]自动报价的功能。懒惰也有类似的表现吗?

例如

member __.Quoted ([<ReflectedDefinitionAttribute>] quotation:Expr<'T>) = ... 
member __.Thunked ([<LazyAttribute>] thunk:Lazy<'T>) = ... 

我想我可以用之类的东西

member __.Quoted ([<ReflectedDefinitionAttribute>] quotation:Expr<'T>) = 
Lazy (evaluate (<@ fun () -> %quotation @>)) // evaluate using Unquote or similar

但这不是很昂贵吗?

更新

我发现了一个黑客,这不是我想要的,但它给出了正确的行为。

type Signal = Signal with
member __.Return x = x
member __.Delay (f:unit -> _) = f
let a = Signal { return randint }
let b = Signal { return randint }
let c = Signal { return a() + b() }

没有什么比ReflectedDefinition属性更能自动将事情变成延迟的Lazy<'T>计算了。

你说得对,自动引用论点可以达到这样的效果。您可以使用(非常有限的(LeafExpressionConverter.EvaluateQuotation对某些有限类型的表达式执行此操作,但正如您所注意到的,这将是低效的。以下是概念验证(但您不能在分支中调用自定义函数,因为这使用了LINQ表达式(:

open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Linq.RuntimeHelpers
type A = 
static member If<'T>(c:bool,
[<ReflectedDefinition>] t:Expr<'T>,
[<ReflectedDefinition>] f:Expr<'T>) = 
if c then LeafExpressionConverter.EvaluateQuotation t :?> 'T
else LeafExpressionConverter.EvaluateQuotation f :?> 'T
A.If(1 = 2, 0, 1)

在实践中,我认为更合理的方法是只使用内置的Lazy<'T>值。F#有一个(不广为人知(lazy关键字,它为您创建这些关键字提供了一个更好的语法:

let iff c (t:Lazy<_>) (f:Lazy<_>) = 
if c then t.Value else f.Value
iff (1 = 2) 
(lazy (printfn "true"; 41)) 
(lazy (printfn "false"; 42))

最新更新