None 比 null 更邪恶吗?

发布于 2024-10-07 22:57:48 字数 104 浏览 3 评论 0原文

在 F# 中,他们没有空值并且不想支持它是一件大事。程序员仍然必须为 None 提供案例,类似于 C# 程序员必须检查 != null。

None 真的比 null 更邪恶吗?

In F# its a big deal that they do not have null values and do not want to support it. Still the programmer has to make cases for None similar to C# programmers having to check != null.

Is None really less evil than null?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

伊面 2024-10-14 22:57:48

null 的问题在于您几乎可以在任何地方使用它,即引入无效状态,这既不是预期也不是有道理。

拥有'a option始终是一件明确的事情。您声明操作可以产生 Some 有意义的值或 None,编译器可以强制正确检查和处理这些值。

通过不鼓励 null 而支持 'a option 类型,您基本上可以保证程序中的任何值在某种程度上都是有意义的。如果某些代码被设计为使用这些值,则您不能简单地传递无效值,并且如果存在 option 类型的函数,您将必须涵盖所有可能性。

The problem with null is that you have the possibility to use it almost everywhere, i.e. introduce invalid states where this is neither intended nor makes sense.

Having an 'a option is always an explicit thing. You state that an operation can either produce Some meaningful value or None, which the compiler can enforce to be checked and processed correctly.

By discouraging null in favor of an 'a option-type, you basically have the guarantee that any value in your program is somehow meaningful. If some code is designed to work with these values, you cannot simply pass invalid ones, and if there is a function of option-type, you will have to cover all possibilities.

夜还是长夜 2024-10-14 22:57:48

当然是少邪恶啦!

如果您不检查 None,那么大多数情况下您的应用程序中都会出现类型错误,这意味着它将无法编译,因此它不会因 NullReferenceException 而崩溃(因为 None 会转换为 null)。

例如:

let myObject : option<_> = getObjectToUse() // you get a Some<'T>, added explicit typing for clarity
match myObject with
| Some o -> o.DoSomething()
| None -> ... // you have to explicitly handle this case

仍然可以实现类似 C# 的行为,但不太直观,因为您必须明确地说“忽略这可能是 None”:

let o = myObject.Value // throws NullReferenceException if myObject = None

在 C# 中,您不必考虑变量的情况为空,因此您可能只是忘记进行检查。与上面相同的示例:

var myObject = GetObjectToUse(); // you get back a nullable type
myObject.DoSomething() // no type error, but a runtime error

编辑:Stephen Swensen 是绝对正确的,我的示例代码有一些缺陷,是匆忙编写的。固定的。谢谢你!

Of course it is less evil!

If you don't check against None, then it most cases you'll have a type error in your application, meaning that it won't compile, therefore it cannot crash with a NullReferenceException (since None translates to null).

For example:

let myObject : option<_> = getObjectToUse() // you get a Some<'T>, added explicit typing for clarity
match myObject with
| Some o -> o.DoSomething()
| None -> ... // you have to explicitly handle this case

It is still possible to achieve C#-like behavior, but it is less intuitive, as you have to explicitly say "ignore that this can be None":

let o = myObject.Value // throws NullReferenceException if myObject = None

In C#, you're not forced to consider the case of your variable being null, so it is possible that you simply forget to make a check. Same example as above:

var myObject = GetObjectToUse(); // you get back a nullable type
myObject.DoSomething() // no type error, but a runtime error

Edit: Stephen Swensen is absolutely right, my example code had some flaws, was writing it in a hurry. Fixed. Thank you!

陌生 2024-10-14 22:57:48

假设我向您展示了这样一个函数定义:

val getPersonByName : (name : string) -> Person

当您传入数据存储中不存在的人的name 时,您认为会发生什么?

  • 该函数是否抛出 NotFound 异常?
  • 它返回 null 吗?
  • 如果人不存在,它会创造人吗?

如果不阅读代码(如果您有权访问它)、阅读文档(如果有人好心地编写了它),或者只是调用该函数,您将无从得知。这基本上就是空值的问题:它们的外观和行为就像非空值一样,至少在运行时是这样。

现在,假设您有一个带有此签名的函数:

val getPersonByName : (name : string) -> option<Person>

这​​个定义非常明确地表明了所发生的情况:您要么会带回一个人,要么不会,并且此类信息以函数的数据类型进行传达。 通常,与潜在的空值相比,您可以更好地保证处理选项类型的两种情况。

我想说选项类型比空值要仁慈得多。

Let's say I show you a function definition like this:

val getPersonByName : (name : string) -> Person

What do you think happens when you pass in a name of a person who doesn't exist in the data store?

  • Does the function throw a NotFound exception?
  • Does it return null?
  • Does it create the person if they don't exist?

Short of reading the code (if you have access to it), reading the documentation (if someone was kindly enough to write it), or just calling the function, you have no way of knowing. And that's basically the problem with null values: they look and act just like non-null values, at least until runtime.

Now let's say you have a function with this signature instead:

val getPersonByName : (name : string) -> option<Person>

This definition makes it very explicit what happens: you'll either get a person back or you won't, and this sort of information is communicated in the function's data type. Usually, you have a better guarantee of handling both cases of a option type than a potentially null value.

I'd say option types are much more benevolent than nulls.

一梦等七年七年为一梦 2024-10-14 22:57:48

在 F# 中,他们没有空值并且不想支持它,这是一件大事。程序员仍然必须为 None 提供案例,类似于 C# 程序员必须检查 != null。

None 真的比 null 更邪恶吗?

每次在 C# 中取消引用对象时,null 都会引入潜在的运行时错误源 (NullRefereceException),而 None 会强制您创建源F# 中显式的运行时错误。

例如,对给定对象调用 GetHashCode 会导致 C# 静默注入运行时错误源:

class Foo {
  int m;
  Foo(int n) { m=n; }
  int Hash() { return m; }
  static int hash(Foo o) { return o.Hash(); }
};

相比之下,F# 中的等效代码预计不会产生 null

type Foo =
  { m: int }
  member foo.Hash() = foo.m

let hash (o: Foo) = o.Hash()

如果您确实想要在 F# 中使用可选值,那么您将使用 option 类型,并且必须显式处理它,否则编译器将给出警告或错误:

let maybeHash (o: Foo option) =
  match o with
  | None -> 0
  | Some o -> o.Hash()

您仍然可以获得 NullReferenceException 在 F# 中通过绕过类型系统(这是互操作所必需的):

> hash (box null |> unbox);;
System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicFunctions.UnboxGeneric[T](Object source)
   at <StartupCode$FSI_0021>.$FSI_0021.main@()
Stopped due to error

In F# its a big deal that they do not have null values and do not want to support it. Still the programmer has to make cases for None similar to C# programmers having to check != null.

Is None really less evil than null?

Whereas null introduces potential sources of run-time error (NullRefereceException) every time you dereference an object in C#, None forces you to make the sources of run-time error explicit in F#.

For example, invoking GetHashCode on a given object causes C# to silently inject a source of run-time error:

class Foo {
  int m;
  Foo(int n) { m=n; }
  int Hash() { return m; }
  static int hash(Foo o) { return o.Hash(); }
};

In contrast, the equivalent code in F# is expected to be null free:

type Foo =
  { m: int }
  member foo.Hash() = foo.m

let hash (o: Foo) = o.Hash()

If you really wanted an optional value in F# then you would use the option type and you must handle it explicitly or the compiler will give a warning or error:

let maybeHash (o: Foo option) =
  match o with
  | None -> 0
  | Some o -> o.Hash()

You can still get NullReferenceException in F# by circumventing the type system (which is required for interop):

> hash (box null |> unbox);;
System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicFunctions.UnboxGeneric[T](Object source)
   at <StartupCode$FSI_0021>.$FSI_0021.main@()
Stopped due to error
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文