我正在尝试使用计算表达式来创建类似于构建器的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.
}
Run Code Online (Sandbox Code Playgroud)
如何更改此代码以便我可以引用该childNodes分配以将其传递给nodes自定义运算符?
您当前的问题是,您需要将[<ProjectionParameter>]属性放在您希望能够访问计算表达式的变量空间的自定义运算符的任何参数上.但是,一旦你添加了这个,你会发现你有一些不匹配类型的问题.一般来说,我同意rmunn:计算表达式不一定适合你的问题,所以你应该强烈考虑使用不同的机制.
但是,如果你坚持推进,那么这是帮助你调试的一个技巧.看起来你想要写
node "something" {
let! childNodes = ([some expression]:Node list)
nodes childNodes
}
Run Code Online (Sandbox Code Playgroud)
所以创建一个像这样的虚拟构建器(看似无用的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
}
Run Code Online (Sandbox Code Playgroud)
并且你会看到它expr的报价大致相当于:
builder.Nodes(
builder.Bind([],
fun childNodes -> builder.Yield childNodes),
fun childNodes -> childNodes)
Run Code Online (Sandbox Code Playgroud)
因此,在您的真实构建器中,您需要具有兼容签名的方法(例如Nodes,第二个参数必须接受一个函数,并且第一个参数必须与返回类型兼容Bind,等等).当您尝试使用虚拟构建器启用其他工作流时,您可以看到它们如何去除并发现其他约束.