首页 > 解决方案 > 为什么 None 在 F# 中自动更改为 null

问题描述

在 F# 交互中尝试以下代码时

> let a = None
- let b = (a, Some 1);;    
> b;;
val it : 'a option * int option = (null, Some 1)

它表明 b 具有类型'a option * int option, b 的类型是正确的。但是,元组的第一个元素的值为null,而不是None,为什么?

当尝试验证元组的第一个元素是否真的为空时

printfn "%s" (match b with (null, _) -> "null" | _ -> "not null");;

它给出了以下错误

错误 FS0043:类型“a option”没有“null”作为正确的值

当尝试获取元组中的第一个值时,

let c = fst b;;

它给

错误 FS0030:值限制。值“c”已被推断为具有通用类型 val c : '_a option添加类型注释。

标签: f#

解决方案


该值的内部表示None确实是一个null值。但这是内部表示,编译器将null和识别None为两个完全不同的值,因此您无法比较它们,因为它们是不同的类型。这就是为什么你得到:error FS0043

这其实是需要注意的:

let a = None
let b = (a, Some 1) 

let print  v = printfn "%A" v

sprintf "%A" a     |> print   // "<null>"
sprintf "%A" b     |> print   // "(None, Some 1)"
sprintf "%O" a     |> print   // "<null>"
sprintf "%O" b     |> print   // "(, Some(1))"
string       a     |> print   // ""
string       b     |> print   // "(, Some(1))"
a      .IsNone     |> print   // true
a      .IsSome     |> print   // false
a      .GetType()  |> print   // System.NullReferenceException: Object reference not set to an instance of an object.
a      .ToString() |> print   // System.NullReferenceException: Object reference not set to an instance of an object.
(fst b).ToString() |> print   // System.NullReferenceException: Object reference not set to an instance of an object.
(snd b).ToString() |> print   // "Some(1)"

...因为在值上调用某些方法None会引发可怕的NullReference异常并且转换为字符串也是不稳定的。

关于error FS0030基本上值不能是通用的。这已在 SO 中多次讨论。

来自可区分联合的值似乎以特殊方式处理,它们似乎被授予例外,例如这些是通用的并且仍然可以:

type MyDU<'A, 'B> =
| ValNo
| ValA of 'A
| ValB of 'B

let v1 = ValNo   // MyDU<'a,'b> double generic but Ok
let v2 = ValA 1  // MyDU<int,'a> generic but Ok
let v3 = ValB 1  // MyDU<'a,int> generic but Ok

但这些都不好

let valNo() = ValNo
let valA  a = ValA a
let valB  b = ValB b

let w1 = valNo()   // MyDU<'_a,'_b> double generic not Ok
let w2 = valA 1  // MyDU<int,'_a> generic not Ok
let w3 = valB 1  // MyDU<'_a,int> generic not Ok

推荐阅读