我试图使用计算表达式来创建类似构建器的DSL,但是当我尝试使用let assignments来帮助编写内容时,我收到一个编译错误,指出无法找到这样的赋值。这是一个例子:
type Node =
{
Key: Option<string>
Children: List<Node>
XPathFromParent: string
}
let defaultNode =
{
Key = None;
Children = [];
XPathFromParent = ".//somePath"
}
type NodeBuilder(xpath: string) =
member self.Yield(item: 'a): Node = defaultNode
member this.xpath = xpath
[<CustomOperation("xpath_from_parent")>]
member __.XPathFromParent (node, x) = {node with XPathFromParent = x}
[<CustomOperation("nodes")>]
member __.Nodes (node, x) = {node with Children = x}
[<CustomOperation("key")>]
member __.MidasMeasurementKey (node, x) = {node with Key = x}
member this.Bind(x, f) = f x
let node xpath = NodeBuilder(xpath)
let rootNode = node ".//somePath" {
let! childNodes =
[
node "somepath" {
nodes []
};
node "someOtherPath" {
nodes []
}
]
nodes childNodes // The value or constructor 'childNodes' is not defined.
}
如何更改此代码,以便我可以引用childNodes
作业将其传递到nodes
自定义运算符?
答案 0 :(得分:5)
您当前的问题是,您需要在自定义运算符的任何参数上放置[<ProjectionParameter>]
属性,以便能够访问计算表达式的变量空间。但是,一旦你添加了这个,你会发现你有一些不匹配类型的问题。一般来说,我同意rmunn:计算表达式不一定适合你的问题,所以你应该强烈考虑使用不同的机制。
但是,如果你坚持推进,那么这是帮助你调试的一个技巧。看起来你想要写
node "something" {
let! childNodes = ([some expression]:Node list)
nodes childNodes
}
所以创建一个像这样的虚拟构建器(看似无用的Quote
方法是关键):
type DummyNodeBuilder(xpath:string) =
[<CustomOperation("nodes")>]
member __.Nodes (node:Node, [<ProjectionParameter>]x) = node // Note: ignore x for now and pass node through unchanged
member __.Yield(_) = Unchecked.defaultof<_> // Note: don't constrain types at all
member __.Bind(_,_) = Unchecked.defaultof<_> // Note: don't constrain types at all
member __.Quote() = ()
let node xpath = DummyNodeBuilder xpath
let expr =
node "something" {
let! childNodes = [] : Node list
nodes childNodes
}
你会看到expr
的报价大致相当于:
builder.Nodes(
builder.Bind([],
fun childNodes -> builder.Yield childNodes),
fun childNodes -> childNodes)
所以在您的真实构建器中,您需要具有兼容签名的方法(例如Nodes
的第二个参数必须接受一个函数,并且第一个参数必须与返回类型{{1等等)。当您尝试使用虚拟构建器启用其他工作流时,您可以看到它们如何去除并发现其他约束。