Подтвердить что ты не робот

Разве не меньше зла, чем нуль?

В F # очень важно, что они не имеют нулевых значений и не хотят его поддерживать. Тем не менее программист должен делать случаи для None, аналогичные программистам на С#, которые должны проверить!= Null.

Является ли кто-нибудь менее злым, чем null?

4b9b3361

Ответ 1

Конечно, это менее зло!

Если вы не проверяете 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

По-прежнему возможно достичь поведения, подобного С#, но оно менее интуитивно, поскольку вы должны явно сказать "игнорировать, что это может быть None":

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

В С# вы не обязаны рассматривать случай, когда ваша переменная имеет значение null, поэтому возможно, что вы просто забыли сделать чек. Тот же пример, что и выше:

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

Изменить: Стивен Свенсен абсолютно прав, мой примерный код имел некоторые недостатки, писал его в спешке. Исправлена. Спасибо!

Ответ 2

Проблема с null заключается в том, что у вас есть возможность использовать ее почти везде, т.е. ввести недопустимые состояния, где это не предназначено и не имеет смысла.

Наличие 'a option всегда является явным. Вы заявляете, что операция может либо выражать значение Some значимое значение, либо None, которое компилятор может обеспечить, чтобы его проверяли и обрабатывали правильно.

Отказываясь null в пользу типа 'a option, вы в основном гарантируете, что любое значение в вашей программе каким-то образом имеет смысл. Если какой-либо код предназначен для работы с этими значениями, вы не можете просто передавать недопустимые, а если есть функция типа option, вам придется покрыть все возможности.

Ответ 3

Скажем, я покажу вам определение функции следующим образом:

val getPersonByName : (name : string) -> Person

Как вы думаете, что происходит, когда вы передаете name человека, которого нет в хранилище данных?

  • Вызывает ли функция исключение NotFound?
  • Он возвращает null?
  • Создает ли человек, если они не существуют?

Не прочитав код (если у вас есть к нему доступ), прочитав документацию (если кто-то был достаточно любезен, чтобы написать ее) или просто вызвал функцию, у вас нет способа узнать. И это в основном проблема с нулевыми значениями: они выглядят и действуют так же, как ненулевые значения, по крайней мере до времени выполнения.

Теперь скажем, что у вас есть функция с этой сигнатурой:

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

Это определение делает очень ясным, что происходит: вы либо вернете человека, либо не хотите, и эта информация передается в виде данных функции. Обычно у вас есть лучшая гарантия обработки обоих случаев типа опции, чем потенциально нулевое значение.

Я бы сказал, что типы опций гораздо более доброжелательны, чем нули.

Ответ 4

В F # очень важно, что они не имеют нулевых значений и не хотят его поддерживать. Тем не менее программист должен делать случаи для None, аналогичные программистам на С#, которые должны проверить!= Null.

Является ли кто-нибудь менее злым, чем null?

В то время как null вводит потенциальные источники ошибок во время выполнения (NullRefereceException) каждый раз, когда вы разыскиваете объект на С#, None заставляет вас явно указывать источники ошибок во время выполнения в F #.

Например, вызов GetHashCode для данного объекта приводит к тому, что С# тихо вводит источник ошибки во время выполнения:

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>[email protected]()
Stopped due to error