我所说的扩展计算表达式是指具有通过 CustomOperation 定义的自定义关键字的计算表达式。属性。
阅读时 extended computation expressions ,我遇到了@kvb 的非常酷的 IL DSL:
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
开头成员,但我还没有找到任何引用资料来验证这一点。如果上面的例子太技术性,这里有一个类似的 DSL,其中列出了幻灯片的组件,但没有
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
我有几个密切相关的问题:
For
的情况下定义或使用扩展计算表达式成员(即提供一个小的完整示例)?如果它们不再是 monad,我不会太担心,我对它们在开发 DSL 方面很感兴趣。 let!
中使用扩展计算表达式吗?和 return!
?如果是,是否有任何理由不这样做?我问这些问题是因为我没有遇到任何使用 let!
的例子。和 return!
. 最佳答案
很高兴您喜欢 IL 示例。了解表达式如何脱糖的最好方法可能是查看 spec (虽然它有点密集......)。
在那里我们可以看到类似的东西
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
,这是因为如果作用域中有变量(例如,因为你使用了一些 lets
,或者在 for 循环中等),那么你会得到 Yield (v1,v2,...)
但是 Zero
显然不能这样使用。请注意,这意味着添加一个多余的 let x = 1
进入托马斯的lr
示例将无法编译,因为 Yield
将使用 int
类型的参数调用而不是 unit
.还有一个技巧可以帮助理解计算表达式的编译形式,即(ab)使用 F# 3 中计算表达式的自动引用支持。只需定义一个什么都不做
Quote
成员(member)和制作 Run
只需返回其参数:member __.Quote() = ()
member __.Run(q) = q
现在您的计算表达式将评估为其脱糖形式的引用。这在调试时非常方便。
关于f# - 不带 for..in..do 的扩展计算表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14110532/