没有for..in.do的扩展计算表达式 [英] Extended computation expressions without for..in..do
问题描述
我所说的扩展计算表达式是指通过 CustomOperation定义的具有自定义关键字的计算表达式。 a>属性。
阅读扩展了计算表达式,我通过@kvb遇到了非常酷的IL DSL:
When reading about extended computation expressions, I come across very cool IL DSL by @kvb:
let il = ILBuilder()
// will return 42 when called
// val fortyTwoFn : (unit -> int)
let fortyTwoFn =
il {
ldc_i4 6
ldc_i4_0
ldc_i4 7
add
mul
ret
}
我想知道如何在不使用 for..in..do
构造的情况下进行操作。我的直觉是它以 x.Zero
成员开头,但是我还没有找到任何引用来验证这一点。
I wonder how the operations compose without using for..in..do
construct. My gut feeling is that it starts with x.Zero
member, but I haven't found any reference to verify that.
如果上面的示例过于技术性,这是一个类似的DSL,其中列出了幻灯片的组件,而没有 for..in..do
:
If the example above is too technical, here is a similar DSL where components of a slide are listed without for..in..do
:
page {
title "Happy New Year F# community"
item "May F# continue to shine as it did in 2012"
code @"…"
button (…)
} |> SlideShow.show
我有几个密切相关的问题:
I have a few closely related questions:
- 如何在没有
For
成员的情况下定义或使用扩展计算表达式(即提供一个小的完整示例)? / em>我不再担心它们不再是单子了,我对它们开发DSL感兴趣。 - 我们可以将扩展计算表达式与
let!
和return!
?如果是,是否有任何理由不这样做?我问这些问题,因为我没有遇到使用let!
和返回!
。
- How does one define or use extended computation expressions without
For
member (i.e. provide a small complete example)? I don't worry much if they aren't monads any longer, I'm interested in them in developing DSLs. - Can we use extended computation expressions with
let!
andreturn!
? If yes, is there any reason of not doing so? I ask these questions because I haven't encountered any example usinglet!
andreturn!
.
推荐答案
我很高兴您喜欢IL示例。了解表达式如何简化的最佳方法可能是查看规格(虽然有点密集...)。
I'm glad you liked the IL example. The best way to understand how expressions are desugared is probably to look at the spec (though it's a bit dense...).
在这里我们可以看到
C {
op1
op2
}
按以下方式删除:
T([<CustomOperator>]op1; [<CustomOperator>]op2, [], fun v -> v, true) ⇒
CL([<CustomOperator>]op1; [<CustomOperator>]op2, [], C.Yield(), false) ⇒
CL([<CustomOperator>]op2, [], 〚 [<CustomOperator>]op1, C.Yield() |][], false) ⇒
CL([<CustomOperator>]op2, [], C.Op1(C.Yield()), false) ⇒
〚 [<CustomOperator>]op2, C.Op1(C.Yield()) 〛[] ⇒
C.Op2(C.Op1(C.Yield()))
为什么使用 Yield()
而不是 Zero
,这是因为范围内是否存在变量(例如因为您使用了一些 let
,或者在for循环中,等等),那么您将得到 Yield(v1,v2,...)
但 0
显然不能用这种方式。请注意,这意味着将多余的 let x = 1
添加到Tomas的 lr
示例中将无法编译,因为<$ c将使用类型为 int
而不是 unit
的参数调用$ c> Yield 。
As to why Yield()
is used rather than Zero
, it's because if there were variables in scope (e.g. because you used some lets
, or were in a for loop, etc.), then you would get Yield (v1,v2,...)
but Zero
clearly can't be used this way. Note that this means adding a superfluous let x = 1
into Tomas's lr
example will fail to compile, because Yield
will be called with an argument of type int
rather than unit
.
还有另一个技巧可以帮助理解计算表达式的编译形式,即(ab)在F#3中使用对计算表达式的自动报价支持。什么都不做的 Quote
成员并使 Run
只是返回其参数:
There's another trick which can help understand the compiled form of computation expressions, which is to (ab)use the auto-quotation support for computation expressions in F# 3. Just define a do-nothing Quote
member and make Run
just return its argument:
member __.Quote() = ()
member __.Run(q) = q
现在,您的计算表达式将根据其减法形式的引用来求值。调试内容时,这可能非常方便。
Now your computation expression will evaluate to the quotation of its desugared form. This can be pretty handy when debugging things.
这篇关于没有for..in.do的扩展计算表达式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!