Tod*_*wen 17 f# computation-expression
F#计算表达式具有以下语法:
ident { cexpr }
Run Code Online (Sandbox Code Playgroud)
ident构建器对象在哪里(此语法取自Don Syme的2007年博客条目).
在我看到的所有示例中,构建器对象是单例实例,无状态引导.Don给出了定义一个名为的构建器对象的示例attempt:
let attempt = new AttemptBuilder()
Run Code Online (Sandbox Code Playgroud)
我的问题:为什么F#不AttemptBuilder直接在计算表达式中使用该类?当然,符号可以像实例方法调用一样轻松地去除静态方法调用.
使用实例值意味着理论上可以实例化同一类的多个构建器对象,可能以某种方式参数化,甚至(天堂禁止)具有可变的内部状态.但我无法想象这将是多么有用.
更新:我上面引用的语法表明构建器必须显示为单个标识符,这是误导性的,可能反映了该语言的早期版本.最新的F#2.0语言规范将语法定义为:
expr { comp-or-range-expr }
Run Code Online (Sandbox Code Playgroud)
这清楚地表明任何表达式(评估为构建器对象)都可以用作构造的第一个元素.
byt*_*ter 15
你的假设是正确的; 可以参数化构建器实例,并且随后可以在整个计算中使用参数.
我使用这种模式来构建一个数学证明树到某个计算.每个结论都是问题名称,计算结果和基础结论(引理)的N树的三倍.
让我提供一个小例子,删除证明树,但保留一个问题名称.我们称它为注释,因为它似乎更合适.
type AnnotationBuilder(name: string) =
// Just ignore an original annotation upon binding
member this.Bind<'T> (x, f) = x |> snd |> f
member this.Return(a) = name, a
let annotated name = new AnnotationBuilder(name)
// Use
let ultimateAnswer = annotated "Ultimate Question of Life, the Universe, and Everything" {
return 42
}
let result = annotated "My Favorite number" {
// a long computation goes here
// and you don't need to carry the annotation throughout the entire computation
let! x = ultimateAnswer
return x*10
}
Run Code Online (Sandbox Code Playgroud)
这只是一个灵活性问题.是的,如果要求Builder类是静态的,那么它会更简单,但它确实需要一些开发人员的灵活性,而不会在过程中获得太多.
例如,假设您要创建用于与服务器通信的工作流.在代码的某处,您需要指定该服务器的地址(Uri,IPAddress等).在哪种情况下,您需要/想要在单个工作流程中与多个服务器进行通信?如果答案为"无",那么使用构造函数创建构建器对象更有意义,该构造函数允许您传递服务器的Uri/IPAddress,而不必通过各种函数不断传递该值.在内部,构建器对象可能会将值(服务器的地址)应用于工作流中的每个方法,从而创建类似(但不完全是)Reader monad的内容.
使用基于实例的构建器对象,您还可以使用继承来创建具有一些继承功能的构建器的类型层次结构.我还没有看到有人在实践中做过这样的事情,但是再一次 - 灵活性就是人们需要的时候,你不会有静态类型的构建器对象.
另一种选择是使用单例区分联合,如:
type WorkFlow = WorkFlow with
member __.Bind (m,f) = Option.bind f m
member __.Return x = Some x
Run Code Online (Sandbox Code Playgroud)
然后你可以直接使用它
let x = WorkFlow{ ... }
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1573 次 |
| 最近记录: |