F# 是否支持 'call-by-name' 语义?
Does F# support 'call-by-name' semantics?
一段时间以来,F# 支持使用 [<ReflectedDefinitionAttribute>]
auto-quote 的能力。有没有类似的懒惰?
例如
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
但这不会很昂贵吗?
更新:
我发现了一个 hack,它不是我想要的,但它给出了正确的行为。
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))
一段时间以来,F# 支持使用 [<ReflectedDefinitionAttribute>]
auto-quote 的能力。有没有类似的懒惰?
例如
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
但这不会很昂贵吗?
更新:
我发现了一个 hack,它不是我想要的,但它给出了正确的行为。
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))