2009-12-06 59 views
2

是否有编译器实现的原因,为什么记录不能具有AllowNullLiteralAttribute属性或者这是一个选择的约束?为什么F#记录不允许有AllowNullLiteralAttribute?

我确实看到这个约束强制更干净的代码有时但并非总是如此。

[<AllowNullLiteralAttribute>] 
type IBTreeNode = { 
    mutable left : IBTreeNode; mutable right : IBTreeNode; mutable value : int} 
with 
    member this.Insert x = 
     if x < this.value then 
      if this.left = null then 
       this.left <- {left = null; right = null; value = x} 
      else 
       this.left.Insert x 
     else 
      if this.right = null then 
       this.right <- {left = null; right = null; value = x} 
      else 
       this.right.Insert x 

// Would be a lot of boilerplate if I wanted these public 
[<AllowNullLiteralAttribute>] 
type CBTreeNode(value) = 
    let mutable left = null 
    let mutable right = null 
    let mutable value = value 
with 
    member this.Insert x = 
     if x < value then 
      if left = null then 
       left <- CBTreeNode(x) 
      else 
       left.Insert x 
     else 
      if right = null then 
       right <- CBTreeNode(x) 
      else 
       right.Insert x 

增加了一个不可变版本的皱眉上的可变性人群。在这种情况下,速度大约快30%。

type OBTree = 
    | Node of OBTree * OBTree * int 
    | Null 
with 
    member this.Insert x = 
     match this with 
     | Node(left, right, value) when x <= value -> Node(left.Insert x, right, value) 
     | Node(left, right, value) -> Node(left, right.Insert x, value) 
     | Null -> Node(Null, Null, x) 
+3

顺便说一下,有什么特别的原因,为什么你需要一个可变的树?我个人认为,不可变的语言会更加简洁,易于使用,而且在功能语言中更具惯用性。 – Juliet 2009-12-06 19:44:10

+0

没有任何可变树会变得非常复杂。以上是一个简单的例子,但是当您试图实现插入和删除时,像没有重叠范围树的东西会变得混乱。如何处理删除级联节点或插入合并到多个其他节点的节点的情况。这种树结构本质上是可变的。 – gradbot 2009-12-06 20:24:07

+1

“您如何处理删除级联节点或插入合并到多个其他节点的节点的情况。” - 只是为了funsies(也许也是为了rep讨价还价!),你问另一个关于如何将你的可变树转换为不可变树的问题。 – Juliet 2009-12-06 20:31:20

回答

3

我只能大胆地猜测,但语言似乎采取的立场是,使用null是能够避免的(如果你需要的功能,你可以随时使用option型)的东西,所以使用null实际上应该限于与其他.NET语言进行交互。因此,F#特定类型不允许使用null

+0

约束绝对不在CLR级别:'AllowNullLiteral'适用于类,记录类型是(密封)类。我期望它可能来自OCAML(出于上述原因)。 – 2009-12-06 19:42:36

2

嗯,记录被翻译成标准的.NET类像其他结构,所以我没有看到技术原因。

我想这个决定是相当哲学的 - Null值是未初始化的,未定义的值,在有状态计算中被替换的东西。在F#等功能上下文中要避免这些。所以我认为记录类型作为一个“功能结构”(不是标准的与.NET兼容)并不意味着携带这些不起作用的数据,但用户需要手动编码(`选项类型等) 。

这也可以让你推理你的代码和例如检查所有可能值的模式匹配,而不必处理null值。

至于你的代码:你真的需要它是必要的吗?那么

data Tree = 
    | Node of int * Tree * Tree 
    | Nil 
+1

顺便说一句,Tony Hoare在今年夏天举行了关于null ref的演示文稿:http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare – Stringer 2009-12-06 19:58:02

+0

以上只是一个人为的例子。我主要关心的是互操作性。几乎所有我曾参与过的重大项目都有大量的互操作性。 – gradbot 2009-12-06 20:51:45

相关问题