Я читал о (не) удобстве иметь null
вместо (например) Maybe
. Прочитав эту статью , я убедился, что было бы намного лучше использоватьMaybe
(или что-то подобное). Однако я удивлен, увидев, что все «общеизвестные» императивные или объектно-ориентированные языки программирования по-прежнему используют null
(что обеспечивает неконтролируемый доступ к типам, которые могут представлять значение «ничего»), и это Maybe
в основном используется в функциональных языках программирования.
В качестве примера рассмотрим следующий код C #:
void doSomething(string username)
{
// Check that username is not null
// Do something
}
Здесь что-то плохо пахнет ... Почему мы должны проверять, является ли аргумент нулевым? Разве мы не должны предполагать, что каждая переменная содержит ссылку на объект? Как видите, проблема в том, что по определению почти все переменные могут содержать нулевую ссылку. Что если бы мы могли решить, какие переменные являются «обнуляемыми», а какие нет? Это сэкономило бы нам много усилий при отладке и поиске «NullReferenceException». Представьте, что по умолчанию никакие типы не могут содержать нулевую ссылку . Вместо этого вы бы прямо заявили, что переменная может содержать нулевую ссылку , только если она вам действительно нужна. Это идея может быть. Если у вас есть функция, которая в некоторых случаях дает сбой (например, деление на ноль), вы можете вернутьMaybe<int>
, явно указав, что результатом может быть int, но тоже ничего! Это одна из причин, почему предпочтение может быть вместо нуля. Если вас интересуют другие примеры, тогда я предлагаю прочитать эту статью .
Факты таковы, что, несмотря на недостатки, заключающиеся в том, что большинство типов по умолчанию обнуляются, большинство языков программирования ОО фактически делают это. Вот почему я задаюсь вопросом о:
- Какие аргументы вы бы использовали
null
вместо своего языка программированияMaybe
? Есть ли причины вообще или это просто «исторический багаж»?
Пожалуйста, убедитесь, что вы понимаете разницу между null и Maybe, прежде чем ответить на этот вопрос.
null
и его концепция не существует (IIRC Haskell является одним из таких примеров).null
в них долгое время. Нелегко просто бросить это.Ответы:
Я считаю, что это в первую очередь исторический багаж.
Самый выдающийся и самый старый язык с
null
C и C ++. Но здесьnull
имеет смысл. Указатели по-прежнему довольно числовые и низкоуровневые концепции. И, как сказал кто-то другой, в мышлении программистов на C и C ++ необходимость явно указывать, что указатель может бытьnull
, не имеет смысла.Второй в очереди идет Java. Учитывая, что разработчики Java пытались приблизиться к C ++, чтобы они могли упростить переход с C ++ на Java, они, вероятно, не хотели возиться с такой основной концепцией языка. Кроме того, для реализации явного выполнения
null
потребуется гораздо больше усилий, поскольку необходимо проверить, правильно ли установлена ненулевая ссылка после инициализации.Все остальные языки такие же, как Java. Они обычно копируют то, как это делают C ++ или Java, и, учитывая, какова базовая концепция неявных
null
ссылочных типов, становится действительно трудно разработать язык, использующий явныйnull
.источник
null
, думаю , удаление не было бы таким большим изменением.std::string
не может бытьnull
.int&
не может бытьnull
.int*
Может, и C ++ позволяет Неконтролируемый доступ к ней по двум причинам: 1. потому что C сделал и 2. потому что вы должны понимать , что вы делаете при использовании сырых указателей в C ++.На самом деле,
null
это отличная идея. Учитывая указатель, мы хотим указать, что этот указатель не ссылается на допустимое значение. Поэтому мы берем одну ячейку памяти, объявляем ее недействительной и придерживаемся этого соглашения (соглашение, которое иногда применяется с segfaults). Теперь, когда у меня есть указатель, я могу проверить, содержит ли онNothing
(ptr == null
) илиSome(value)
(ptr != null
,value = *ptr
). Я хочу, чтобы вы поняли, что это эквивалентноMaybe
типу.Проблемы с этим:
Во многих языках система типов здесь не помогает гарантировать ненулевую ссылку.
Это исторический багаж, так как многие основные императивные языки или языки ООП имеют прогрессивные улучшения в своих системах типов только по сравнению с предшественниками. Небольшие изменения имеют то преимущество, что новые языки легче изучать. C # является основным языком, который представил инструменты уровня языка для лучшей обработки нулей.
Разработчики API могут возвращаться
null
в случае неудачи, но не в случае ссылки на саму вещь в случае успеха. Часто вещь (без ссылки) возвращается напрямую. Такое выравнивание одного уровня указателя делает невозможным использованиеnull
в качестве значения.Это просто лень на стороне дизайнера, и с ней нельзя справиться, не навязывая правильное вложение с правильной системой типов. Некоторые люди могут также попытаться оправдать это с соображениями производительности, или с наличием дополнительных проверок (коллекция может вернуть
null
или сам элемент, но и обеспечитьcontains
метод).В Haskell есть аккуратный взгляд на
Maybe
тип как монаду. Это облегчает составление преобразований для содержащегося значения.С другой стороны, языки низкого уровня, такие как C, почти не рассматривают массивы как отдельный тип, поэтому я не уверен, чего мы ожидаем. В языках ООП с параметризованным полиморфизмом
Maybe
тип, проверяемый во время выполнения, довольно прост для реализации.источник
null
меньше, чем в прошлом?null
s » - я говорю оNullable
типах и??
операторе по умолчанию. Это не решает проблему прямо сейчас в присутствии унаследованного кода, но это является шагом на пути к лучшему будущему.Nullable
.Насколько я понимаю, это
null
была необходимая конструкция для абстрагирования языков программирования от сборки. 1 Программистам нужна была возможность указать, что указатель или значение регистра былоnot a valid value
иnull
стало общим термином для этого значения.Укрепляя пункт, который
null
является просто соглашением для представления концепции, фактическое значение, котороеnull
используется, чтобы иметь возможность / может варьироваться в зависимости от языка программирования и платформы.Если вы разрабатываете новый язык и хотите избежать,
null
но использоватьmaybe
вместо этого, я бы рекомендовал более описательный термин, напримерnot a valid value
или,navv
чтобы указать намерение. Но имя этого не-значения является отдельным понятием от того, должны ли вы позволить не-значениям существовать даже на вашем языке.Прежде чем вы сможете принять решение по любому из этих двух пунктов, вам необходимо определить, что значение
maybe
будет означать для вашей системы. Вы можете обнаружить, что это просто переименованиеnull
смысла,not a valid value
или вы можете обнаружить, что оно имеет другую семантику для вашего языка.Аналогичным образом, решение о том, проверять ли доступ по
null
доступу или по ссылке, является еще одним дизайнерским решением на вашем языке.Чтобы предоставить немного истории,
C
было неявное предположение, что программисты понимают, что они пытаются делать при манипулировании памятью. Поскольку это была превосходная абстракция для ассемблера и предшествующих ему императивных языков, я рискну предположить, что мысль о защите программиста от неверной ссылки не приходила им в голову.Я считаю, что некоторые компиляторы или их дополнительные инструменты могут обеспечить меру проверки на недопустимый доступ к указателю. Таким образом, другие отметили эту потенциальную проблему и приняли меры для защиты от нее.
Должны ли вы это разрешить или нет, зависит от того, чего вы хотите добиться от своего языка, и какую степень ответственности вы хотите передать пользователям вашего языка. Это также зависит от вашей способности создать компилятор для ограничения этого типа поведения.
Итак, чтобы ответить на ваши вопросы:
«Что за аргументы…» - Ну, это зависит от того, что вы хотите, чтобы язык делал. Если вы хотите смоделировать доступ к голому металлу, вы можете разрешить это.
"Это просто исторический багаж?" Возможно, возможно нет.
null
безусловно, имеет / имеет значение для ряда языков и помогает управлять выражением этих языков. Исторический прецедент мог повлиять на более поздние языки и их разрешение,null
но немного взмахнуть руками и объявить концепцию бесполезным историческим багажом.1 См. Эту статью в Википедии, хотя Хоэру отдают должное нулевые значения и объектно-ориентированные языки. Я считаю, что императивные языки развивались по другому генеалогическому древу, чем Алгол.
источник
null
).object o = null; o.ToString();
для меня просто компилируется, без ошибок и предупреждений в VS2012. ReSharper жалуется на это, но это не компилятор.Если вы посмотрите на примеры в статье, которую вы цитировали, большая часть использования
Maybe
не сокращает код. Это не устраняет необходимость проверкиNothing
. Разница лишь в том, что это напоминает вам сделать это через систему типов.Обратите внимание, я говорю «напомнить», а не силой. Программисты ленивы. Если программист убежден, что значение не может быть
Nothing
, он будет разыменовыватьMaybe
без проверки, точно так же, как теперь разыменовывает нулевой указатель. Конечным результатом является преобразование исключения с нулевым указателем в исключение «разыменованный пустой возможно».Тот же принцип человеческой природы применяется в других областях, где языки программирования пытаются заставить программистов что-то делать. Например, разработчики Java пытались заставить людей обрабатывать большинство исключений, что привело к множеству шаблонов, которые либо молча игнорируют, либо слепо распространяют исключения.
То , что делает
Maybe
это хорошо, когда много решений сделаны с помощью сопоставления с образцом и полиморфизм вместо явных проверок. Например, вы можете создать отдельные функцииprocessData(Some<T>)
иprocessData(Nothing<T>)
, что вы не можете сделать сnull
. Вы автоматически перемещаете обработку ошибок в отдельную функцию, что очень желательно в функциональном программировании, где функции передаются и оцениваются лениво, а не всегда вызываются сверху вниз. В ООП предпочтительным способом отсоединения кода обработки ошибок являются исключения.источник
const
, но сделать его необязательным. Некоторые виды низкоуровневого кода, такие как связанные списки, например, было бы очень раздражающим для реализации с объектами, не допускающими значения NULL.map :: Maybe a -> (a -> b)
иbind :: Maybe a -> (a -> Maybe b)
определенные в нем значения, чтобы вы могли продолжить и выполнять дальнейшие вычисления по большей части с повторным преобразованием с использованием оператора if else. ИgetValueOrDefault :: Maybe a -> (() -> a) -> a
позволяет обрабатывать обнуляемый случай. Это намного элегантнее, чем сопоставление с образцом вMaybe a
явном виде.Maybe
Это очень функциональный способ мышления о проблеме - есть вещь, и она может иметь или не иметь определенное значение. Однако в объектно-ориентированном смысле мы заменяем эту идею вещи (независимо от того, имеет ли она ценность или нет) на объект. Понятно, что объект имеет значение. Если это не так, мы говорим, что объект естьnull
, но на самом деле мы имеем в виду, что объекта вообще нет. Ссылка у нас на объект указывает на ничто. ПереводMaybe
в концепцию ОО не делает ничего нового - на самом деле, он просто делает код более загроможденным. Вы все еще должны иметь нулевую ссылку для значенияMaybe<T>
, Вы по-прежнему должны делать нулевые проверки (на самом деле, вы должны делать намного больше нулевых проверок, загромождая ваш код), даже если они теперь называются «возможно проверки». Конечно, вы напишите более надежный код, как утверждает автор, но я бы сказал, что это только потому, что вы сделали язык намного более абстрактным и тупым, требующим уровня работы, который в большинстве случаев не нужен. Я бы охотно использовал исключение NullReferenceException время от времени, чем имел дело со спагетти-кодом, выполняющим проверку Maybe каждый раз, когда я получаю доступ к новой переменной.источник
Maybe<T>
должен разрешитьnull
в качестве значения, потому что значение может не существовать. Если у меня естьMaybe<MyClass>
, и у него нет значения, поле значения должно содержать нулевую ссылку. Для этого нет ничего, что было бы достоверно безопасно.Maybe
может быть абстрактный класс с двумя классами, которые наследуются от него:Some
(у которого есть поле для значения) иNone
(у которого нет этого поля). Таким образом, значение никогда не бываетnull
.Концепция
null
легко может быть прослежена до C, но проблема не в этом.Мой повседневный язык - C #, и я бы придерживался
null
одного отличия. C # имеет два вида типов, значений и ссылок . Значения никогда не могут бытьnull
, но бывают моменты, когда я хотел бы выразить мнение, что ни одно значение не является идеальным. Чтобы сделать это, C # используетNullable
типы, поэтомуint
будет значение иint?
значение, допускающее значение NULL. Вот как я думаю, что ссылочные типы также должны работать.Также смотрите: Нулевая ссылка не может быть ошибкой :
источник
Я думаю, это связано с тем, что функциональное программирование очень беспокоит типы , особенно типы, которые сочетают другие типы (кортежи, функции как первоклассные типы, монады и т. Д.), Чем объектно-ориентированное программирование (или, по крайней мере, изначально).
Современные версии языков программирования, о которых я думаю, вы говорите (C ++, C #, Java), все основаны на языках, которые не имели какой-либо формы общего программирования (C, C # 1.0, Java 1). Без этого вы все еще можете испечь некоторую разницу между обнуляемыми и ненулевыми объектами в языке (например, ссылки на C ++, которые не могут быть
null
, но также ограничены), но это гораздо менее естественно.источник
Я думаю, что основная причина заключается в том, что для защиты программы от повреждения данных требуется относительно небольшое количество нулевых проверок. Если программа пытается использовать содержимое элемента массива или другого места хранения, которое, как предполагается, было записано с допустимой ссылкой, но не было, лучшим вариантом будет выброшенное исключение. В идеале исключение будет точно указывать, где возникла проблема, но важно то, что возникает какое-то исключение до того, как пустая ссылка сохраняется где-то, что может привести к повреждению данных. Если метод не хранит объект, не пытаясь использовать его каким-либо способом, попытка использовать объект сама по себе представляет своего рода «нулевую проверку».
Если кто-то хочет убедиться, что нулевая ссылка, которая появляется там, где ее не должно быть, вызовет конкретное исключение, отличное от
NullReferenceException
этого, часто необходимо будет включать нулевые проверки повсюду. С другой стороны, простое обеспечение того, что какое-либо исключение произойдет до того, как нулевая ссылка может привести к «повреждению» сверх того, что уже было сделано, часто требует относительно небольшого количества тестов - тестирование обычно требуется только в тех случаях, когда объект будет хранить ссылка, не пытаясь использовать ее, и либо нулевая ссылка перезапишет действительную, либо это заставит другой код неправильно интерпретировать другие аспекты состояния программы. Такие ситуации существуют, но не все так часто; большинство случайных нулевых ссылок попадут очень быстропроверять их или нет .источник
"
Maybe
," как написано, это конструкция более высокого уровня, чем ноль. Используя больше слов, чтобы определить это, может быть, это «указатель на вещь или указатель на ничто, но компилятору еще не дали достаточно информации, чтобы определить, какая именно». Это заставляет вас постоянно проверять каждое значение, если только вы не создадите спецификацию компилятора, которая достаточно умна, чтобы не отставать от написанного вами кода.Вы можете сделать реализацию Maybe с языком, который имеет нули легко. C ++ имеет один в виде
boost::optional<T>
. Сделать эквивалент нуля с Возможно очень сложно. В частности, если у меня естьMaybe<Just<T>>
, я не могу присвоить ему значение null (потому что такого понятия не существует), в то время какT**
в языке с нулем очень легко назначить значение null. Это заставляет его использоватьMaybe<Maybe<T>>
, что полностью допустимо, но заставит вас сделать еще много проверок, чтобы использовать этот объект.Некоторые функциональные языки используют Maybe, потому что для null требуется либо неопределенное поведение, либо обработка исключений, что не является простым понятием для сопоставления с синтаксисами функционального языка. Возможно, в таких функциональных ситуациях роль гораздо лучше исполняется, но в процедурных языках ноль - это король. Это не вопрос правильного и неправильного, просто вопрос того, как проще заставить компьютер делать то, что вы от него хотите.
источник