Null, великий и ужасный

    Ошибка дизайна


    Именно так и никак иначе: null в C# — однозначно ошибочное решение, бездумно скопированное из более ранних языков.


    1. Самое страшное: в качестве значения любого ссылочного типа может использоваться универсальный предатель — null, на которого никак не среагирует компилятор. Зато во время исполнения легко получить нож в спину — NullReferenceException. Обрабатывать это исключение бесполезно: оно означает безусловную ошибку в коде.
    2. Перец на рану: сбой (NRE при попытке разыменования) может находится очень далеко от дефекта (использование null там, где ждут полноценный объект).
    3. Упитанный пушной зверек: null неизлечим — никакие будущие нововведения в платформе и языке не избавят нас от прокаженного унаследованного кода, который физически невозможно перестать использовать.

    Этот ящик Пандоры был открыт еще при создании языка ALGOL W великим Хоаром, который позднее назвал собственную идею ошибкой на миллиард долларов.


    Лучшая историческая альтернатива


    Разумеется, она была, причем очевидная по современным меркам


    1. Унифицированный Nullable для значимых и ссылочных типов.
    2. Разыменование Nullable только через специальные операторы (тернарный — ?:, Элвиса — ?., coalesce — ??), предусматривающие обязательную обработку обоих вариантов (наличие или отсутствие объекта) без выбрасывания исключений.
    3. Примеры:
      object o = new object(); // ссылочный тип - корректная инициализация
      object o = null; // ссылочный тип - ошибка компиляции, так как null недопустим
      object? n = new object; // nullable тип - корректная инициализация
      object? n = null; // nullable тип - корректная инициализация
      object o = n; // ссылочный тип - ошибка компиляции, типы object и object? несовместимы
      object o = n ?? new object(); // разыменование с fallback значением (coalesce), дополнительное значение будет вычислено только если n != null
      Type t = n ? value.GetType() : typeof(object); // специальный тернарный оператор - value означает значение n, если оно не null
      Type? t = n ? value.GetType(); // бинарная форма оператора ? - возвращает null, если первый операнд null, иначе вычисляет второй операнд и возвращает его, завернутого в nullable
    4. В этом случае NRE отсутствует по определению: возможность присвоить или передать null определяется типом значения, конвертация с выбросом исключения отсутствует.

    Самое трагичное, что все это не было откровением и даже новинкой уже к моменту проектирования первой версии языка. Увы, тогда матерых функциональщиков в команде Хейлсберга не было.


    Лекарства для текущей реальности


    Хотя прогноз очень серьезный, летального исхода можно избежать за счет применения различных практик и инструментов. Способы и их особенности пронумерованы для удобства ссылок.


    1. Явные проверки на null в операторе if. Очень прямолинейный способ с массой серьезных недостатков.


      1. Гигантская масса шумового кода, единственное назначение которого — выбросить исключение поближе к месту предательства.
      2. Основной сценарий, загроможденный проверками, читается плохо
      3. Требуемую проверку легко пропустить или полениться написать
      4. Проверки можно добавлять отнюдь не везде (например, это нельзя сделать для автосвойств)
      5. Проверки не бесплатны во время выполнения.

    2. Атрибут NotNull. Немного упрощает использование явных проверок


      1. Позволяет использовать статический анализ
      2. Поддерживается R#
      3. Требует добавления изрядного количества скорее вредного, чем бесполезного кода: в львиной доле вариантов использования null недопустим, а значит атрибут придется добавлять буквально везде.

    3. Паттерн проектирования Null object. Очень хороший способ, но с ограниченной сферой применения.


      1. Позволяет не использовать проверок на null там, где существует эквивалент нуля в виде объекта: пустой IEnumerable, пустой массив, пустая строка, ордер с нулевой суммой и т.п. Самое впечатляющее применение — автоматическая реализация интерфейсов в мок-библиотеках.
      2. Бесполезен в остальных ситуация: как только вам потребовалось отличать в коде нулевой объект от остальных — вы имеете эквивалент null вместо null object, что является уже двойным предательством: неполноценный объект, который даже NRE не выбрасывает.

    4. Конвенция о возврате живых объектов по умолчанию. Очень просто и эффективно.


      1. Любой метод или свойство, для которых явно не заявлена возможность возвращать null, должны всегда предоставлять полноценный объект. Для поддержания достаточно выработки хорошей привычки, например, посредством ревью кода.


      2. Разработчики сторонних библиотек ничего про ваше соглашение не знают
      3. Нарушения соглашения выявить непросто.

    5. Конвенция о стандартных способах явно указать что свойство или метод может вернуть null: например, префикс Try или суффикс OrDefault в имени метода. Органичное дополнение к возврату полноценных объектов по умолчанию. Достоинства и недостатки те же.


    6. Атрибут CanBeNull. Добрый антипод-близнец атрибута NotNull.


      1. Поддерживается R#
      2. Позволяет помечать явно опасные места, вместо массовой разметки по площадям как NotNull
      3. Неудобен в случае когда null возвращается часто.

    7. Операторы C# (тернарный, Элвиса, coalesce)


      1. Позволяют элегантно и лаконично организовать проверку и обработку null значений без потери прозрачности основного сценария обработки.
      2. Практически не упрощают выброс ArgumentException при передаче null в качестве значения NotNull параметра.
      3. Покрывают лишь некоторую часть вариантов использования.
      4. Остальные недостатки те же, что и у проверок в лоб.

    8. Тип Optional. Позволяет явно поддержать отсутствие объекта.


      1. Можно полностью исключить NRE
      2. Можно гарантировать наличие обработки обоих основных вариантов на этапе компиляции.
      3. Против легаси этот вариант немного помогает, вернее, помогает немного.
      4. Во время исполнения помимо дополнительных инструкций добавляется еще и memory traffic

    9. Монада Maybe. LINQ для удобной обработки случаев как наличия, так и отсутствия объекта.


      1. Сочетает элегантность кода с полнотой покрытия вариантов использования.
      2. В сочетании с типом Optional дает кумулятивный эффект.
      3. Отладка затруднена, так как с точки зрения отладчика вся цепочка вызовов является одной строкой.
      4. Легаси по-прежнему остается ахиллесовой пятой.

    10. Программирование по контракту.


      1. В теории почти идеал, на практике все гораздо печальнее.
      2. Библиотека Code Contracts скорее мертва, чем жива.
      3. Очень сильное замедление сборки, вплоть до невозможности использовать в цикле редактирование-компиляция-отладка.

    11. Пакет Fody/NullGuard. Автоматические проверки на null на стероидах.


      1. Проверяется все: передача параметров, запись, чтение и возврат значений, даже автосвойства.
      2. Никакого оверхеда в исходном коде
      3. Никаких случайных пропусков проверок
      4. Поддержка атрибута AllowNull — с одной стороны это очень хорошо, а с другой — аналогичный атрибут у решарпера другой.
      5. С библиотеками, агрессивно использующими null, требуется довольно много ручной работы по добавлению атрибутов AllowNull
      6. Поддержка отключения проверки для отдельных классов и целых сборок
      7. Используется вплетение кода после компиляции, но время сборки растет умеренно.
      8. Сами проверки работают только во время выполнения.
      9. Гарантируется выброс исключения максимально близко к дефекту (возврату null туда, где ожидается реальный объект).
      10. Тотальность проверок помогает даже при работе с легаси, позволяя как можно быстрее обнаружить, пометить и обезвредить даже null, полученный из чужого кода.
      11. Если отсутствие объекта допустимо — NullGuard сможет помочь только при попытках передать его куда не следует.
      12. Вычистив дефекты в тестовой версии, можно собрать промышленную из тех же исходников с отключенными проверками, получив нулевую стоимость во время выполнения при гарантии сохранения всей прочей логики.

    12. Ссылочные типы без возможности присвоения null (если добавят в одну из будущих версий C#)


      1. Проверки во время компиляции.
      2. Можно полностью ликвидировать NRE в новом коде.
      3. В реальности не реализовано, надеюсь, что только пока
      4. Единообразия со значимыми типами не будет.
      5. Легаси достанет и здесь.


    Итоги


    Буду краток — все выводы в таблице:


    Настоятельная рекомендация Антипаттерн На ваш вкус и потребности
    4, 5, 7, 11, 12 (когда и если будет реализовано) 1, 2 3, 6, 8, 9, 10

    На предвосхищение ООП через 20 лет не претендую, но дополнениям и критике буду очень рад.


    Обновление


    добавил примеры кода к утопической альтернативе.

    Share post

    Similar posts

    Comments 290

      0
      Можно перейти на F# и… начать лепить вместо null везде Unchecked.defaultof<'T>
        0
        Что, на сколько я понимаю, не избавляет от теоретической возможности появления null где-нибудь и от необходимости проверки значений на null. При программировании на Scala тоже раздражает существование null-ей при том, что есть Nothing.
          0
          Насколько я понял в F# это сделано как в Kotlin, т. е. чистый F# null-safe
            0
            Да, на нём можно писать в функциональном стиле, а Unchecked добавлен для совместимости с C#.

            Интересно, что оператор isNull добавили только в версии 4.0 языка.
              0
              В смысле «чистый F#»? Без внешних, написанных на C# .Net библиотек? А какой от него практический толк тогда? Спортивная информатика? Или там весь основной функционал (включая функции работы с внешними web-сервисам, файловой системой, графикой и прочим) продублирован в null-safe манере?
                0
                В прямом. Да. Не знаю. Может быть. Может только стандартная библиотека, опять же, в Kotlin сделали так: https://habrahabr.ru/post/309462/#comment_9801496, как сделали в F# я не знаю.
          +4
          Бедный этот null. Добавьте в статью опрос, как часто вы имеете NullReferenceException? Лично вот я — не чаще раза в неделю, а то и реже, использую только ?:,?.. и ??.. Я конечно, стараюсь null не возвращать и не передавать, но это на уровне смысла методов скорее, чем претензия к языку.

          Проблема не в том что разыменование бросает исключение. Есть и другие исключения, тоже летят. Выход за границы массива например. А представьте, нету null. Тогда все как в анекдоте про буратино и яблоки. Добавлять метод IsValid? Те же проверки же. По-моему суть вопроса не в наличии маркера (отсутствия чего-либо) null, а в том что языки программирования и компиляторы различают объекты от ссылок. Переменная на то и переменная, что она может быть изменена.
            +3
            Если нет null обычно есть что-то другое, например Maybe, гораздо хуже когда null есть, а операторов из п.7 нет.
              +3
              Ну и получаем (с Maybe) тот же if null, только обернутый.

              Моя мысль скорее в том, что языки из 90-х (и их наследники) оперируют в императивном стиле, и, следовательно, есть переменные. C# не исключение, и ждать от него обязательств что у переменной всегда должен быть объект… зачем? Там выше про F# сказали. Давно уже хотел присмотреться к нему как следует, но всё времени нет. Теперь его приоритет увеличил. Может следующий проект на нём буду :) Если Xamarin F#-ready (а это вроде так).
                +3

                С Maybe у вас есть возможность требовать обязательной обработки как наличия, так и отсуствия значения, не оставляя места для ошибок в коде, ведущих к NRE.

                  0
                  if null тоже разный бывает, pattern matching (в Haskell, ML, Rust, Scala) мне кажется более изящным и интуитивным решением. Maybe без nullable типов может и должен быть, а вот наоборот гораздо сложнее.
                    0
                    Монадические операции еще изящнее, ИМХО (от do до sequence).
                    +1
                    языки из 90-х (и их наследники) оперируют в императивном стиле, и, следовательно, есть переменные
                    Скорее, тут корни идут от указателей и ручного управления памятью.
                      0
                      Не идут. Присвоение к null не обязывает объект быть удаленным.
                        +1
                        Но после удаления хорошо бы присвоить null, если переменная ещё в области видимости.
                          0
                          Дело не в частностях. А в том, что мозги разработчиков были воспитаны на unmanaged языках, и объектную ссылку никто не воспринимал иначе, как указатель, со всеми вытекающими последствиями.

                          В школе их учили Паскалю, или Аде кодом типа
                          var
                            p: ^TMyObject;
                          begin
                            New(p);
                            p.DoSomething;
                            Dispose(p);
                          end.
                          в воздухе ещё не витали концепции монад, optional-ов и т.п.
                            0
                            1. Задним числом все умные.
                            2. Откуда инфа, кто что как воспринимал? Я вот таких обобщений не делаю.

                            В этом ответвлении я уже всё сказал, и совсем в другую сторону. Язык и то, во что он компилируется далеко не только предысторией привыкших к чему-то программистов связаны. И те или иные решения в нём приняты не потому что так у всех, или все привыкли, но еще и потому что бывают и коммерческие ограничения (время, бюджет), и технические (что мог тогда .NET и что сейчас), и много всего разного. А по-вашему весь контекст ситуации — это программисты на Паскале.
                    +5

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


                    1. Если null в этом месте не нужен, то гораздо лучше поддерживать non-nullable типы с помощью компилятора и не иметь NRE.
                    2. Если null в этом месте возможен по бизнес-логике, то гораздо лучше требовать обработку обоих случаев (наличие и отсутсвие значения) на этапе компиляции и не иметь NRE.
                      +1
                      NRE в продакшене… Думаете я посреди ночи проснусь в холодному поту, если приснится? В разработке крупной платежной системы лет 10 назад работал, на С++ писал — и нормально с этим жили, читали дампы, а специальные демоны рестартовали сервисы. У конечного пользователя все это неприятно, но и в Java, и в Swift есть тот самый null.

                      Да и вообще причем тут NRE. Объясните, почему другие исключения не удостаиваются чести иметь столько внимания и языки, в которых они не случаются? К чему такое избирательство? Все это в случае конкретного языка и конкретно одного исключения больше напоминает упражнения для ума.

                      По поводу compile-time проверок. Еще раз. Переменная (ссылка) может иметь любое значение, это значит что выполняющее (программу) устройство зарезервировало под него кусок места в памяти. Вот этот кусок в памяти может указывать на что угодно, на умерший, невалидный объект, или валидный. Отказ от null в языке означал бы по факту отказ от ссылок, и превратил бы язык в фактически интерпретируемый, потому что в компайл-тайме объекты не создаются.
                        +1
                        Раскрою немного мысль. Все ухищрения по поводу оборачивания языковых конструкций — начиная со smart_ptr из С++, это создание собственного интепретатора в языке и написание кода на нём. Дело не в оверхеде, дело в том, что если не нравится язык — лучше его сменить, чем и без того обрастаемые костылями старые языки латать. Благо сейчас много чего быстро работает.

                        А что касается NRE, я их тоже не люблю, просто это вообще не самая большая проблема в моей практике.
                          +3
                          Понимаете, по вашей логике получается, что т. к. NRE не самая ваша большая проблема, то не нужно чинить этот косяк в дизайне языка.

                          Not null reference type планировалось добавить в C#7, но потом отложили эту фичу до лучших времен (лучшие времена ожидаются с С#8, но там опять же… кто знает что будет). Вот народ и негодует, что средство для практически автоматического улучшения качества программ и упрощения разработки еще прийдется черт знает сколько ждать.
                          +4
                          > Объясните, почему другие исключения не удостаиваются чести иметь столько внимания и языки, в которых они не случаютс

                          Потому, что NRE — это всегда ошибка разработчика, а какое-нибудь UnauthorizedAccessException — нет.
                            0
                            А какое-нибудь index out of range, или division by zero, или stack overflow? Тред не читай@на коммент отвечай?
                            Любой язык будет допускать ошибки разработчика. Даже русский.

                            Мне лично самому не нравится, когда в рантайме летят исключения, касающиеся языка. Очень раздражает. Это с того момента, как я с плюсов пересел на Java/C#. Хотя это намного, намного лучше, чем core dump, gdb и stack frames.

                            Какой смысл предъявлять к дизайну языка претензии через 10 лет после его создания, он для своего времени и так больше чем мог сделал. Я больше по этому поводу озадачен.
                              0
                              > index out of range

                              Не знаю о чём вы, я использую foreach

                              > division by zero

                              Нет

                              > stack overflow

                              Придумаете как победить на уровне языка — дайте знать.

                              > Какой смысл предъявлять к дизайну языка претензии через 10 лет после его создания

                              Почему 10 лет? Язык называется «C# 6.0», в названии, кстати, и ответ на «какой смысл» заложен.
                                0
                                Научи́те избегать division by zero без дополнительной проверки операнда на нуль, пожалуйста.

                                  0
                                  А 2 инта складывать без OverflowException научить?

                                  оборачиваете в try и выдаёте пользователю, что некорректные данные.
                                    +2
                                    Спасибо, поржал.

                                    А NRE в `try` в шарпе запрещено оборачивать? Разница-то в чем?

                                      0
                                      Я же написал,NRE это ошибка разработчика. Пользователю бесполезно о ней ошибку выбрасывать. А данные в знаменателе поправить обычно может.
                                        0
                                        Вообще, NRE оборачивать не то, что запрещено, но довольно бессмысленно. Т.е. обернув вызов чистой функции какого-то расчёта и получив division by zero я уверен, что ошибка изолирована и приложение дальше работает корректно, а получив NRE — нет, и дальше продолжать выполнение некорректно.
                                          0
                                          Не вижу никакой разницы между NRE и DBZ в случае чистой функции.
                                          Какая разница, какой-то метод ошибочно вернул 0 в переменную «кол-во процессоров в системе» и дальше упало при делении, или вернул null в переменную CPUManager.
                                            0
                                            Разница в том, откуда приходят данные. Циферки, обычно, приходят извне (их там миллионы, косяки всегда есть). А null — это где-то присвоить забыли в своём коде.

                                            Одна ошибка на миллион пользовательских данных — это нормально, нужно быть готовым обрабатывать.
                                            Вернуть 0 в переменную «кол-во процессоров в системе» — это экзотика.
                                            Миллион CPUManager, один из которых null — это надо талант иметь, скорее у нас память битая.
                                            Все(один) CPUManager null(WTF, а это вообще кто-то тестил?) — зачем так жить?

                                            Можно придумать какие-то исключения, но, в целом, DBZ — не кодерская ошибка, но «бытовая». А NRE — точно какая-то хрень, которая сама не починиться.
                                            0
                                            если бы получили DBZ из функции, значит функция написана неверено, какой смысл что-то изолировать, если у вас программа написана неверно? хоть какой-то смысл это имеет на границе плагина или чего-то такого что можно безболезненно прибить и оно при этом нам не подконтрольно
                                              0
                                              0) возврат DBZ — часть естественного контракта функции. Функции деления, например.
                                              1) в программе кнопок больше одной, одна может не работать. Главное — побочных эффектов не настругать.
                                              2) Функция может работать в рамках контракта, который мы где-то нарушили. Самое банальное — плохо проверили пользовательский ввод. Нехорошо, но не смертельно — введут ещё раз.
                                                0
                                                возврат DBZ — часть естественного контракта функции. Функции деления, например.


                                                нет

                                                в программе кнопок больше одной, одна может не работать. Главное — побочных эффектов не настругать.


                                                и что? не работает — перепиши

                                                Функция может работать в рамках контракта, который мы где-то нарушили. Самое банальное — плохо проверили пользовательский ввод. Нехорошо, но не смертельно — введут ещё раз.


                                                контракт нарушен -> написано неверено -> смысла работать дальше нет
                                                  0
                                                  > нет

                                                  Это факт, а не вопрос

                                                  > и что? не работает — перепиши

                                                  В следующую итерацию
                                          +1
                                          если у вас ожидается что инты будут переполняться, то, очевидно, использовать механизм исключений для этого глубоко неправильно. Впрочем как и ловля любого SystemException без rethrow
                                            0
                                            Как бы вы написали минимально простой калькулятор, в котором пользователь формулу вводит? Ну, без ловли ArithmeticException.
                                              0
                                              валидировал бы ввод и отселаживал overflow/underflow без эксепций, удивительно просто, да?
                                                0
                                                А как? Получить ArithmeticException способов больше одного: сложение, умножение, деление, логарифм — всё проверками обложить?

                                                SystemException нельзя ловить как таковой, а OverflowException — можно
                                                  0
                                                  А как? Получить ArithmeticException способов больше одного: сложение, умножение, деление, логарифм — всё проверками обложить?


                                                  ну если делаем лабораторку в универе, то можно и не обкладывать )) а какие еще есть варианты? input всегда нужно валидировать

                                                  SystemException нельзя ловить как таковой, а OverflowException — можно


                                                  я попробую раскрыть свою мысль: если мы говорим, например, про деление то контракт у деления должен выгялдить вот так:

                                                  Contract.Require(divisor != 0);

                                                  если мы говорим про overflow то у функции сложения контракт будет вглядеть вот так:

                                                  Contract.Require(int.MaxValue — arg1 > arg2 && int.MaxValue — arg2 > arg1);

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

                                                  надо ли делать так всегда? конечно же нет, иначе будет еще один замечательный чистый язык, на котором никто ничего не пишет, а компромиссы в простых случаях даже не приемлемы, а желанны

                                                  а вообще за всю мою практику из наследников SystemException мне приходилось ловить и проглатывать только TaskCanceledException, все что было связано с арифметикой — это всегда была моя ошибка
                                                    0
                                                    > input всегда нужно валидировать

                                                    Если формула отпарсилась в дерево — инпут годный. Но от переполнения при умножении это не спасёт.

                                                    > Contract.Require(int.MaxValue — arg1 > arg2 && int.MaxValue — arg2 > arg1);

                                                    А для возведения в степень? А во сколько раз дольше ваша проверка будет считаться (да и писаться, с тестами), чем само вычисление?

                                                    Если нам не надо, например, подсвечивать место в формуле, где произошла ошибка, то это чистый оверхед. Программирование ради программирования, выбрасывание которого — необходимость, а не компромисс.
                                                      0
                                                      >Если формула отпарсилась в дерево — инпут годный. Но от переполнения при умножении это не спасёт.

                                                      а инпут к формуле?

                                                      >А во сколько раз дольше ваша проверка будет считаться (да и писаться, с тестами), чем само вычисление?

                                                      один раз — при комипяции. вы вообще слышали про контракты?
                                                        0
                                                        Как вы пользовательский ввод при компиляции проверите?
                                                          0
                                                          воувоу, я все понял. вам нужны лекции «бертран мейер» «основы объектно-ориентированного программирования». там вы узнаете почему контракты и ввод не соотносятся чуть менее чем никак, и почему контракты должны проверяться во время компиляции
                                                            0
                                                            Я спрашивал про пользователя вводящего формулу, это вы контракты притащили.

                                                            Ввод хоть тушкой, хоть чучелком в рантайме проверять придёться на соответствие «int.MaxValue — arg1 > arg2 && int.MaxValue — arg2 > arg1»
                                                              0
                                                              конечно, и это правильно
                                                                +1
                                                                Конечно. Но долго, дорого, и часто не понятно зачем именно нужно.
                                                        0
                                                        >Программирование ради программирования, выбрасывание которого — необходимость, а не компромисс.

                                                        я про это и говорю, но я все же надеялся что мы разбираем абстрактного коня, а не кокретный пример. Возвращаясь к теме: можно найти кучу контрпримеров когда станет плохо от выкидывания null, но это не значит, что не нужно ничего менять
                                                          0
                                                          Я против абстрактных коней. В физике вакуум убирает некоторую погрешность, а в другом это приводит к рассуждениям «лучше быть здоровым и богатым, чем бедным и больным».

                                                          Инструмент должен соответствовать задаче, хоть null, хоть goto.
                                                            0
                                                            ну нет, всегда должен быть недостижимый идеал
                                                              0
                                                              https://www.ted.com/talks/malcolm_gladwell_on_spaghetti_sauce
                                                          0
                                                          >А для возведения в степень?

                                                          ЗЫ. Math.Pow не бросает эксепций, также как и деление у double ))
                                                            0
                                                            Значит у нас будет не double. Не спрашивайте, заказчик попросил.
                                                              0
                                                              тогда не получится в отрицательную степень возвести, тоже выглядит как контракт функции ))
                                            –2
                                            По логике противников NRE stack overflow легко победить запретив рекурсию и так далее.
                                              –1
                                              Нельзя запретить рекурсию, не запертив функции.
                                                0
                                                И?
                                                  0
                                                  и «легко победить» — бред, не имеющий отношение к противникам NRE
                                                    0
                                                    Вы уже утомили хуже горькой редьки отказом понимать весь смысл написанного (открою тайну: бывают языки и без функций). Своими доморощенными классификациями и наколенными соображениями опустили уровень дискуссии на дно.
                                                      0
                                                      Вы уже утомили хуже горькой редьки отказом понимать весь смысл написанного. Бывает и brainfck, только на нём никто не пишет.
                                                  0
                                                  Не думаю что это хорошая идея, но все же, что мешает запретить в языке рекурсию не запрещая функции?
                                                    0
                                                    Можно не запрещать. Достаточно отслеживать циклические вызовы компилятором (чтобы неявную рекурсию убрать) и запретить вызов по указателю (чтобы не проверять в рантайме). Если с первым еще можно смириться (любой рекурсивный алгоритм достаточно тривиально превращается в нерекурсивный), то второе уже серьезно ограничивает область применения такого языка.
                                                      0
                                                      > Достаточно отслеживать циклические вызовы компилятором

                                                      На какую глубину? На сколько такой компилятор будет тормозить?
                                                        0

                                                        Можно прикинуть (очень грубо). Пускай N — количество функций. Наибольшее количество уникальных вызовов (в смысле откуда и что) будет достигаться, если каждая функция будет вызывать каждую. Таким образом для проверки нам потребуется проверить не более N^2 вариантов. Соответственно сложность проверки ограничена O(N^2), что не так уж и много.


                                                        К тому же, фехтование верхом на стульях требует времени: https://xkcd.com/303/

                                                          0
                                                          Соответственно сложность проверки ограничена O(N^2), что не так уж и много.

                                                          Для типичного проекта в 100.000 функций — много.
                                                          Тем более, надо отсекать варианты непрямых вызовов (A → B → C → D → A), а это построение матрицы достижимости на ориентированном графе, что с использованием лучших известных алгоритмов имеет сложность O(N3)
                                                            0
                                                            Нам достаточно проверить ориентированный граф вызовов на ацикличность. Граф без петель (вызов функцией самой себя тривиально проверяется). Сложность этого алгоритма — O(M), где M — количеством ребер, которое ограничено количеством ребер в полном графе, т.е. N*(N-1). Т.е. общая сложность — O(N^2). Вот задача поиска ВСЕХ таких циклов уже другое дело.
                                                              0
                                                              > Вот задача поиска ВСЕХ таких циклов уже другое дело.

                                                              Хотелось бы в ошибках компиляции весь список, конечно.

                                                              И, кстати, не забудьте про неявные вызовы через .net fw, я затейник.

                                                              А вообще, вы почему-то думаете, что граф вызовов статический и известен при компиляции. Это немножко не так. По крайней мере я не представляю как иначе.
                                                                0
                                                                Я в изначальном комментарии сказал, что вызовов по указателю нет. Любой непрямой вызов попадает под это условие.
                                                                  0
                                                                  Это в рамках языка. Из внешнего мира то нас могут вызывать?
                                                                    0

                                                                    А это все имеет смысл только в рамках языка.


                                                                    Компилятор не может отвечать за внешний мир. Любое взаимодействие с внешним миром не является ответственностью компилятора. Компилятор может проверить правильность только того кода, который обрабатывается этим компилятором. Мы можем любой код подменить во время выполнения, и компилятор не может гараниторовать соблюдение контрактов в этом случае.


                                                                    Максимум что может сделать копилятор — запретить использовать неправильный код в случае если компилятору известно о его неправильности.


                                                                    Язык может предоставлять безопасные средства обращения с внешним миром, но это не является гаранитей соблюдения внешним миром контрактов.


                                                                    Условие на невозможность вызовов по указателю нам дает известность графа при компиляции. Внешний мире же тут либо соглашается с контрактами языка, либо...


                                                                    … Либо не соглашается и делает что хочет, но ответственность лежит полностью на внешнем мир

                                                                      0
                                                                      > ответственность лежит полностью на внешнем мир

                                                                      Если я использую внешний мир так, что возникает рекурсия — это не вина внешнего мира. И не вина компилятора.

                                                                      Пока есть стек — его можно переполнить.
                                                              0
                                                              > Для типичного проекта в 100.000 функций — много.
                                                              В типичном проекте не будет глубины в 100.000 вложенностей.

                                                              > надо отсекать варианты непрямых вызовов
                                                              Для этого и достаточно O(N^2).
                                                          0
                                                          Не обязательно даже отслеживать. Если это язык исполняемый строго сверху вниз (как большинство интерпретируемых), просто не добавляем функцию в контекст видимости пока не завершится ее тело. В этом случае любой рекурсивный вызов все равно что вызов необъявленной функции.
                                              0
                                              Переменная (ссылка) может иметь любое значение, это значит что выполняющее (программу) устройство зарезервировало под него кусок места в памяти. Вот этот кусок в памяти может указывать на что угодно, на умерший, невалидный объект, или валидный.


                                              Для программиста ссылка — это ни одним местом не адрес в памяти. Ссылка — это «пульт» объекта с «кнопками» в виде доступных полей, методов, свойств и т.д. Какая к черту память? Адреса в памяти — это сишные указатели. Даже в С++ мухи отделены от котлет.

                                              А нулевая ссылка — это такой пульт, который выглядит как нормальный, но при нажатии кнопки вместо переключения на футбол на месте тебя и твоего дома остаются дымящиеся руины и надгробная плита с надписью «телевизор не найден».
                                                0
                                                Вы свой язык когда-нибудь пробовали писать? Я писал. Он был в 2004 году, без null, и был фактически SQL с классами. На нём было написано штук пять среднего размера программ для работы с данными (всякого рода поиски по различным сущностям). Он был интерпретируемым.

                                                Картинку из Ералаша про «батарейки забыл» лень искать. Если бы я захотел его транслировать в машинный код, в нём был бы null. В противном случае бинарник содержал бы в себе исходник и интерпретатор.
                                                  0
                                                  Если бы я захотел его транслировать в машинный код, в нём был бы null. В противном случае бинарник содержал бы в себе исходник и интерпретатор.


                                                  В огороде бузина, а в Киеве дядька.
                                                    0
                                                    Программистам на диване читают лекции в другом месте.
                                                  0
                                                  Вот не нужно драматизировать, пожалуйста. Нулевая ссылка — это банальный бутафорский пульт, при нажатии кнопки на котором ничего не происходит. Правда, если запустить им в окно, окно разобьется, но бытует мнение, что пульт в этом не виноват.

                                                    0
                                                    Не правда, как минимум если на него нажать, то он сломается и черт его знает чего еще поломает.
                                                      0
                                                      SEGFAULT — это «ничего не происходит» ))
                                                        0
                                                        Похоже у товарища iqiaqqivik свои особенные null'ы после которых ничего не происходит ))
                                                          0
                                                          Ох. А есть ли какая-нибудь принципиальная разница в случае гипотетической транзакции (whatever it means), упали мы в корку, или просто элегантно упали в мессаджбокс «ошибка 512».

                                                          Я вообще адепт идеологии «падать лучше как можно раньше»: эрланг, ОТП, супервизоры, вот это все.
                                                            0
                                                            Есть принципиальная разница между ошибками времени компиляции и выполнения. В языке, где любая ссылка может быть null, компилятор не сможет гарантировать, что в рантайме не будет NRE. Если делать без null, можно разрулить опциональность с помощью соответствующего типа (Option). Никаких NRE не будет в принципе.
                                                              0
                                                              Можно разрулить. Не будет NRE. Я это все понимаю. Я не понимаю, почему это вдруг всегда лучше.

                                                              Ну, кроме аргумента «это же очевидно».
                                                                0

                                                                Аргумент "вместо ошибки периода выполнения непонятно где и когда получаем ошибку периода компиляции по месту дефекта" вы тоже не увидели?

                                                                  0
                                                                  Это не аргумент. Во-первых, «где» в рантайме ничуть не хуже, даже наведенный, если есть стектрейс. «Когда» — да, это весомо. Но цену за строгую типизацию приходится платить тоже немаленькую.

                                                                  Вместо того, чтобы, грубо говоря, вызвать цепочку `a ⇒ b ⇒ c ⇒ d ⇒ e`, словить NRE, по стеку определить, кто виноват и выплюнуть: «не могу этого сделать, „c“ не определен», мне придется заморачиваться наличием у всех акторов сигнатуры, отличающей Empty, или заворачивать это все в Either. Иногда это оправдано. Иногда — нет.

                                                                  И когда это оправдано, мне никто не помешает быть готовым ко всему. А когда нет — меня очень выручает null. Еще раз: ваш аргумент блестяще доказывает несомненную пользу подхода в первом случае. Зачем при этом вымарывать очень удобный иногда null из языка — убейте, не пойму.
                                                                    0

                                                                    Желания убрать null нет, есть желание, чтобы язык поддерживал not nullable ссылки, а для nullable ссылок выдавал ошибку на возможные места возникновения NRE.

                                                                      +2
                                                                      словить NRE


                                                                      Жесть какая.

                                                                      Нафиг вам вообще типы, заморачиваться ими еще. Просто везде принимайте и возвращайте object, в рантайме «словите» ошибку и разберетесь.
                                                                        0
                                                                        Я именно так и делаю, спасибо.

                                                        0
                                                        Хаскель интерпретируемый?
                                                      0
                                                      Я конечно, стараюсь null не возвращать и не передавать, но это на уровне смысла методов скорее, чем претензия к языку.

                                                      Почему бы не починить проблему на уровне языка, которую можно починить на уровне языка?

                                                      Лично я был бы рад не писать повсеместно [NotNull], а использовать корректный тип. null не является валидным значением типа. Это отсутствие значения. И то, что объекты по-умолчанию нулы это явная недоработка.

                                                      Почему просто так скастовать default(int?) в int нельзя, а (null) Person в Person можно? Почему в первом случае мы явно выделяем отдельный тип, а во втором это одно и то же? Ведь как все помним, вся разница между структурами и классами — в семантике копирования ссылки против копирования значения.
                                                        0
                                                        Интересно, а можно ли было ссылочные типы объединить с optional? То есть, иными словами можно ли на уровне языка любой ссылочный тип рассматривать как optional у которого null — значение none?
                                                        Или может быть можно сформулировать иначе: возможно ли (опять на уровне языка) сделать optional таким, чтобы любой ссылочный тип рассматривался как optional, но при этом была возможность явно указать что данный ссылочный тип не может иметь значения null (атрибут NotNull?)
                                                          0

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

                                                          +7
                                                          Хайп вокруг заменителей null напоминает страшный социальный эксперимент, в котором 9 подставных человек называют белое черным, чтобы смутить десятого. Потому что, я бы ещё понял null-aware работу с обычным указателем, без которой компилятор вас изобьет. Но попытка убедить людей в том, что какой-то особый монадический контейнер с точно такой же повсеместной проверкой на IfPresent (или как-то иначе) засоряющий клиентский код это намного лучше — это просто какой-то артхаусный фильм про 'мир сошёл с ума'. Что там, кстати, с NULL в sql, не надумали отказаться? Пусть реальный мир подвинется, в ИТ не может не быть значения, пусть даже это значение НетЗначения.
                                                            +3
                                                            Да тут все проще. Сам null — вполне нормальное явление. Но некоторые типы нуллабельны по умолчанию, некоторые — ненуллабельны. И получается так, что это свойство типа неявное, оно как-бы спрятано внутри самого типа. То есть когда вы имеете дело с типом T, вы заранее не знаете, может ли он быть null, или нет.
                                                            А если бы например при разработке языка ввели правило, что T — это всегда ненуллабельный тип, а скажем T? — всегда нуллабельный, вероятно ясности было бы больше.
                                                              +8

                                                              Дело в том, что все ошибки, связанные с null, можно и нужно обнаруживать на этапе компиляции.

                                                                –1
                                                                Это не так.Вот вам контрпример:
                                                                1) Вы пишите библиотеку
                                                                2) Я спустя год пишу код, который ее использует

                                                                Как вы на этапе компиляции библиотеки отследите все случаи, когда использующий ее код передает в нее null?
                                                                  +6

                                                                  Это "не так" в C# и многих других языках как раз из-за ошибки дизайна.
                                                                  В Haskell NRE невозможен по построению.
                                                                  Тип A требует значение типа A. Тип Maybe A позволяет не иметь значения, но требует обрабатывать и его наличие (Just a), и отсутствие (Nothing).

                                                                    –4
                                                                    Переменная типа Object требует значение типа Object или null, и мог бы требовать обработку null, но мы не будем об этом говорить, лучше поговорим про обфункционаливание всего подряд.

                                                                    Конечно, ошибка дизайна тоже имеется, но она не в null заключается, а в отсутствии понятия «предусловия метода» в подавляющем числе систем модульности в любой экосистеме, хоть в джаве, хоть в дотнете (про .so вообще молчу, позорище из 70-х).
                                                                      0
                                                                      Переменная типа Object требует значение типа Object или null
                                                                      Из-за ошибки дизайна.

                                                                      Без нее переменная типа Object требовала бы значения типа Object, а null допускала бы переменная типа Object? (точно также как и для значимых типов)

                                                                        0
                                                                        Типы аргументов это тоже предусловия и сейчас они не могут выразить non-null без костылей вроде R# и спецатрибутов.
                                                                        +1
                                                                        В хаскелле — очень даже возможен, только компилятор будет ругаться (или не будет, если его немножко запутать) на неисчерпывающий перебор.
                                                                        foo (Just x) = "foo"
                                                                        -- foo Nothing не добавили
                                                                        
                                                                        bar x = case x of (Just y) -> "bar"
                                                                        --              | otherwise не добавили
                                                                        
                                                                        
                                                                        f = foo Nothing
                                                                        b = bar Nothing
                                                                        


                                                                        вылетит исключение Non-exhaustive patterns in case
                                                                          0

                                                                          Насколько я помню, компилятор обломает в обоих случаях и до ошибок периода исполнения дело не дойдет.

                                                                            +1
                                                                            Не обломает. http://ideone.com/jN7nAB

                                                                            foo (Just t) = "just"
                                                                            --foo Nothing    = "nothing"
                                                                            
                                                                            main = do
                                                                              putStrLn "begin..."
                                                                              putStrLn (foo Nothing)
                                                                              putStrLn "end."
                                                                            

                                                                              0
                                                                              А это что? prog: prog.hs:1:1-21: Non-exhaustive patterns in function foo
                                                                                0
                                                                                А это — stderr исполнения программы, куда был направлен вывод обработчика исключений!
                                                                                Сперва в stdout попало «begin...», а затем, не доходя до «end.», прилетела ошибка, которая вывалилась в stderr (в соседний блок на странице).

                                                                                Была бы ошибка компиляции, там ideone совсем по-другому написало бы, — в ещё одном блоке.
                                                                            0
                                                                            В любом тьюринг-полном языке возможен, иначе компилятору придется решать halting problem.

                                                                            Впрочем, это общая проблема анализа на bottom, null тут частный случай.
                                                                          0
                                                                          >Как вы на этапе компиляции библиотеки отследите все случаи, когда использующий ее код передает в нее null?
                                                                          Проверкой всех входных параметров.
                                                                          Проблема в том, что сейчас всё это приходится делать руками, если бы была возможность использовать для этого стандартную языковую конструкцию — было бы намного менее обременительно.
                                                                            0
                                                                            Как вы на этапе компиляции библиотеки отследите все случаи, когда использующий ее код передает в нее null?


                                                                            code contracts
                                                                              0
                                                                              Как вы на этапе компиляции библиотеки отследите все случаи, когда использующий ее код передает в нее null?

                                                                              В том-то и прелесть not-null типов, что это делать будите вы, когда захотите передать в библиотеку null тип, а не автор библиотеки. А коли передается без проверок на наличие объекта — то ССЗБ.

                                                                            –2
                                                                            Если воспринимать `null` так, как его воспринимают в реальном мире, а именно — как отсутствие чего либо, пустой слот — то все типы нуллабельны, вообще-то.

                                                                              +3
                                                                              Как null не воспринимай, в int по умолчанию его присвоить нельзя.
                                                                                0
                                                                                нельзя, потому что язык не позволяет.
                                                                                а если смотреть шире (что и предлагает делать iqiaqqivik), то вполне себе можно
                                                                                  +1
                                                                                  а если смотреть шире (что и предлагает делать iqiaqqivik), то вполне себе можно

                                                                                  И создать себе геморрой еще и со значимыми типами.

                                                                                    0
                                                                                    про какой геморрой вы говорите? NULL — это значение. У него логическая нагрузка другая, это да, но это значение. Третье состояние, если использовать терминологию схемотехники. Ведь никто не возражает против третьего состояния в отношении микросхем, никто не возражает, что у типа данных Variant есть состояние unassigned. Потому что это нормально — отсутствие известного значения. Это не бесплатно, да. Но это нормально
                                                                                      +1
                                                                                      про какой геморрой вы говорите?
                                                                                      Например про необходимость каждому полю и переменной значимых типов где-то хранить флаг отвечающий за наличие значения. Итого выравнивание в структурах/классах слетает, памяти жрется больше, кеш-миссы всегда чаще.
                                                                                      –1
                                                                                      Примерно в этом месте обычно уже начинается проповедь про то, что строгая типизация — это хорошо.

                                                                                      В некоторых (далеко не во всех, впрочем) случаях — это даже правда.

                                                                                      Реальный же мир устроен по другому. Я только на это указал.

                                                                                        +1
                                                                                        Реальный мир устроен так, что функция работает только с определённым набором значений (не пересекающийся с типами в общем случае). В них null может входить, или не входить. Предикаты вы можете проверять на компиляции или вручную. Но вы не должны выполнять функцию при невыполнении её предикатов.

                                                                                        А входит ли «null» или «68543» в конкретный тип (подмножество значений) — это на совести конкретного типа. Тип — это не элемент «реального мира».
                                                                                          0
                                                                                          Реальный мир устроен так, что значение всегда есть (опуская квантовые эффекты, но и там вместо null должна быть другая конструкция, более определённая, чем универсальный null).

                                                                                          Если под null подразумевать «мы не знаем», то это уже не конструкция реального мира, а наша абстрация поверх неё.
                                                                                            0
                                                                                            > всегда есть (опуская

                                                                                            Это замечательно.

                                                                                            null часто подразумевает не «не знаю», а «не применим». Какого цвета электрон? Какой запах у песни?
                                                                                              0
                                                                                              В реальном мире нет этого значения, значит, под него вообще не требуется переменная.
                                                                                              Если у вас есть переменная, хранящая «запах песни» = null, это плохой дизайн )))
                                                                                                0
                                                                                                В реальном мире нет переменных, и типов(каждый объект уникален). Есть характеристики.
                                                                                                  +1
                                                                                                  В реальном мире нет способа (функции), который померяет у музыки запах и вернет null. Несовместимость интерфейсов.
                                                                                                    0
                                                                                                    Вы вообще что называете функцией в реальном мире?
                                                                                                      0
                                                                                                      Ничего не называю. Функцию я написал как ближайший аналог в ЯП.
                                                                                                0
                                                                                                Под «всегда есть» я имел ввиду, что null-значений не бывает.
                                                                                                Если у объекта есть температура, она не-null
                                                                                                  0
                                                                                                  Это как то противоречит тому, что я сказал?
                                                                                                    0
                                                                                                    Да. В хорошем дизайне не должно быть ситуации, в которой в переменную «цвет электрона» кладём значение «не применим». эта информация должна быть на мета-уровне, на уровне схемы данных, а не как значение «null»
                                                                                                      0
                                                                                                      Что значит «в хорошем дизайне»? Мы о реальном мире? Не нравится цвет электрона — скажите размер ноги безногого.
                                                                                                        0
                                                                                                        О программе, моделирующей реальный мир. О плохом дизайне модели, если модель легко может отобразить невозможную ситуацию.
                                                                                                          0
                                                                                                          Так и что там с моделью безногого? Это невозможная ситуация?
                                                                                                            0
                                                                                                            В хорошей модели безногого нет метода, возвращающего отсутствующую ногу, чтобы что-то там у нее измерять.
                                                                                                              0
                                                                                                              В реальном мире объекты уникальны, невозможно составить модель каждого без-чего-то-там.
                                                                                                              Делают одну модель человека.
                                                                                                                0
                                                                                                                > В реальном мире объекты уникальны, невозможно составить модель каждого без-чего-то-там
                                                                                                                Но вы ведь называете это моделью безногого. Зачем модели безногого возможности иметь ноги? Может заодно и возможность наличия колес ему дать, на случай кибернетизации?
                                                                                                                  0
                                                                                                                  > Но вы ведь называете это моделью безногого.

                                                                                                                  *модель, способную описать безногого
                                                                                                                    0
                                                                                                                    > *модель, способную описать безногого
                                                                                                                    А зачем навязывать модель, которая возможно хранит ноги, если нам нужен именно безногий?

                                                                                                                    Так можно уйти в философию и дойти до концептов уровня any UniversalType.getProperty(string propertyName).

                                                                                                                    Но вообще, можно и с опциональной ногой:
                                                                                                                    bool human.applyIfLegPresent(toolCallback);
                                                                                                                      0
                                                                                                                      > А зачем навязывать модель, которая возможно хранит ноги, если нам нужен именно безногий?

                                                                                                                      Не нужен, у нас равноправие и толерантность. Безногих в обувной магазин пускают (так и вижу, тыкаешь в раздел «обувь», а всплывает плашка «а у вас ноги есть?).

                                                                                                                      > Так можно уйти в философию и дойти до концептов уровня any UniversalType.getProperty(string propertyName).

                                                                                                                      dynamic — это практика. А в JS — суровая реальность.

                                                                                                                      > bool human.applyIfLegPresent(toolCallback);

                                                                                                                      Это про интерфейс, а не модель. Ну а внутри то переменная есть?

                                                                                                                      И дальше что? IfKneePresent, ifMiddleFingerPresent? Сразу же написал — »невозможно составить модель каждого без-чего-то-там"
                                                                                                                        0
                                                                                                                        > Безногих в обувной магазин пускают (так и вижу, тыкаешь в раздел «обувь», а всплывает плашка «а у вас ноги есть?)
                                                                                                                        Пускают, но никакого null при доступе к размеру отсутствующих ног они не получают, потому что и доступа этого нет.

                                                                                                                        > Ну а внутри то переменная есть?
                                                                                                                        Нет. У безногого этот метод вернет false и ничего не сделает.

                                                                                                                        > И дальше что? IfKneePresent
                                                                                                                        А чем это отличается от «knee = getKnee(); if (knee != null) {… };», кроме отсутствия null?
                                                                                                                        Да, у человека очень много свойств, и никакая модель не поможет их убрать, если не абстрагироваться.

                                                                                                                        > невозможно составить модель каждого
                                                                                                                        Можно составить модель каждого, кто предусмотрен к обработке в системе. В любой момент времени мы либо знаем тип объекта (и знаем наверняка, что у него можно мерять), либо не знаем (а там у нас возможно щупальценогий крылоклюв). Если интерфейс совпадает — меряем. Если нет — мы бессильны.
                                                                                                          0
                                                                                                          скажите размер ноги безногого
                                                                                                          Какое приложение делаем? По подбору обуви? Не должно оно запускаться у безногого. Если это часть более общего приложения по подбору гардероба, модуль обуви должен быть недоступен для безногого.
                                                                                                            0
                                                                                                            Регистрационную форму соцсети фетишистов.
                                                                                                              0
                                                                                                              Здесь одного null-а недостаточно.
                                                                                                              Сегодня требуют вариант «я безногий инвалид», а завтра захотят «я параноик, поэтому не скажу».
                                                                                                              Тут в любом случае Option, с вариантами, и если вариант — числовой, доступен метод получения значения.
                                                                                                                0
                                                                                                                Не надо придумывать сложностей на ровном месте. В социльной сети фетишистов параноики не регистрируются.

                                                                                                                Если вы под «Option» и «доступен метод» подразумеваете, что при обращении к недоступному методу я получу исключение — так работает null. Хотя, конечно, и у него есть фатальный недостаток.
                                                                                                  –1
                                                                                                  > вместо null должна быть другая конструкция, более определённая, чем универсальный null

                                                                                                  Да почему? Почему вы считаете, что ваша менее универсальная конструкция лучше? В некоторых случаях — лучше, а в некоторых — нет. В конце концов, не нравится — не пользуйся. Но объявлять это ошибкой дизайна как минимум смешно. Мода на монады пройдет так же, как прошла в свое время мода на ООП. Empty в подавляющем большинстве случаев — овердизайн, который только заслоняет суть.

                                                                                                    +2
                                                                                                    Потому что null-object «без запаха» имеет тип, и вместо него нельзя подставить «без цвета».
                                                                                                      –1
                                                                                                      Почему это нельзя? При наличии в системе null очень даже можно. И сразу же из коробки можно получить список всех элементов «без одного признака». Что, мягко говоря, поторебует километров ненужного кода при строгой типизации.

                                                                                                      Шах и мат.
                                                                                                        0
                                                                                                        Я, вообще-то, про паттерн «null object».
                                                                                                      +4
                                                                                                      А может все-таки все наоборот и not-null reference types + optional более явно бы показывало суть, чем текущий дизайн reference types? Если опустить варианты оптимизации производительности через null, как особое значение, то я не вижу не одного случая где бы null'ы делали бы код читабельнее или безопаснее. Можете такой случай привести?
                                                                                                        0
                                                                                                        Легко.

                                                                                                        (Сразу оговорюсь, что такое «делать код безопаснее» я не понимаю, в моем мире безопасным код делает разработчик, поэтому далее — про читабельность.)

                                                                                                        Анкета. 500 вопросов. Ответы бывают самые разные: галочка, радиокнопочка, текст, картинка. Нужна (помимо прочего) функция, получающая среднее количество ответов в анкете. Пойдет? Или желаете чего-нибудь поизощреннее, с промежуточной генерацией кода?
                                                                                                          0
                                                                                                          У вас у анкеты один инстанс подразумевается? Разряженный массив объектов ответов (разных типов и с null вперемешку) и доступ по индексу с последующим кастингом по метаданным анкеты? Хочу читабельностью вдохновиться.

                                                                                                          А в псевдокоде можно? А то не понятно что вам мешает null object пересчитать, вместо null'ов.
                                                                                                            0
                                                                                                            Наследовать надо от BaseAnswer типы CheckBoxAnswer, TextAnswer, ImageAnswer. И заодно туда же — NoAnswer.
                                                                                                              0
                                                                                                              А что именно вы хотите от BaseAnswer в NoAnswer отнаследовать?
                                                                                                                0
                                                                                                                Да, верно. По-хорошему, NoAnswer не нужен. Должна быть коллекция заполненных ответов, map: QuestionId -> BaseAnswer.
                                                                                                              0
                                                                                                              «безопаснее» и удобнее в моем понимании — это перекладывать на компилятор всю рутинную работу, которую компилятор может со 100% точностью сделать гораздо эффективнее человека. Например: проверка ошибок (не давать изменять readonly-поле), представление компилятором более безопасного и читаемого синтаксиса (ссылки, с их как бы автоматическим разыменованием по сравнению с указатеями), генерация кода по определенному паттерну(типа генерация конечного автомата по async..await-синтаксису) и т. п. Это все из той же оперы. Компьютер практически не делает ошибок(если у него есть строгий корректный алгоритм) по сравнению с человеком в такой рутинной работе. Следовательно, преимущества на лицо: гораздо быстрее получаем корректный результат. Просто нужно иметь возможность это удобно выразить в языке.

                                                                                                              П. С. iqiaqqivik, мне как и areht'у хотелось бы пример с пояснением, что вы считаете удобством. Только желательно с упором в «безопасность» т. е. простоту поддержки этого кода другими членами команды.
                                                                                                        0
                                                                                                        В «реальном мире» в случае хорошего дизайна опциональность нужна относительно редко, но в Java/C# она навязана всем, всегда и везде. Вместо редких случаев небольшого оверхеда (и то не факт) нас имеют мы имеем кучу шаблонного кода, заключающегося в проверках на null 95 % аргументов функций.
                                                                                              +4
                                                                                              var r;
                                                                                              var foo = getFoo();
                                                                                              var bar = getBar();
                                                                                              if(foo != null && bar != null){
                                                                                                r = calc(foo, bar);
                                                                                              }

                                                                                              r = do
                                                                                                foo <- getFoo
                                                                                                bar <- getBar
                                                                                                return $ calc foo bar

                                                                                              Где тут проверка на IfPresent?

                                                                                                –2
                                                                                                Вместе с null выплеснули всё остальное императивное программирование? Лихо.
                                                                                                  +3
                                                                                                  Вы так говорите, как будто это что-то плохое.
                                                                                                  0
                                                                                                  liftM2 calc getFoo getBar, пожалуйста.

                                                                                                  И в императивном коде вам надо проверять foo перед получением bar.
                                                                                                  +1

                                                                                                  Основная тема статьи: не как отказаться от null, а как избежать ошибок разыменования и сделать это как можно раньше.


                                                                                                  Что там, кстати, с NULL в sql, не надумали отказаться?

                                                                                                  В sql с null-значения проблемы другого рода: можно получить проблемы с производительностью (например, oracle не индексируют null-значения), можно легко сделать логическую ошибку в предикате запроса (троичная логика не всегда очевидна). И отказ от null — одно из возможных решений.

                                                                                                    +1
                                                                                                    В SQL с null куча своего геммороя.

                                                                                                    Так, например, Oracle считает, что пустая строка и null — это одно и то же.
                                                                                                      0

                                                                                                      Да, писать банальное условие str = '' в oracle противопоказано, можно долго отлаживаться. Кстати, из-за этой "фичи" строковый тип называется varchar2, а varchar не рекомендован к использованию.

                                                                                                        0
                                                                                                        даже так скажу. не надо брать varchar2, надо использовать nvarchar2, потому что иначе серверные сортировки могут очень странно работать для неанглоязычных строк.
                                                                                                    +1
                                                                                                    Мне одному кажется что проблема излишне раздута в статье?

                                                                                                    Все (по крайней мере должны) проверяют входные параметры в функцию и выходные из функции. Работа кода, который делает что-то с пустым объектом обычно бесполезна. Что толку, если вы получили пустой объект? Логика работы программы всё равно нарушена.
                                                                                                    Да и возврат не null, а пустого объекта тоже имеет место быть.
                                                                                                      +1
                                                                                                      Все (по крайней мере должны) проверяют входные параметры в функцию и выходные из функции.

                                                                                                      Да, восемь строк проверки на три строки полезного кода смотрятся просто, чисто и элегантно.
                                                                                                      Проверки выходных параметров и возвращаемых значений в коде не припоминаю за все время работы.
                                                                                                      Посмотрите решение 11 — оно справится с проверками лучше программиста-человека.

                                                                                                        +3
                                                                                                        Все (по крайней мере должны) проверяют входные параметры в функцию и выходные из функции.


                                                                                                        А должен проверять компилятор. В этом, черт возьми, вся соль.
                                                                                                        –1
                                                                                                        Статья не понравилась. Интрига есть (я «почти» согласился, что null — это «однозначно ошибочное решение, бездумно скопированное из более ранних языков»), но в «исторической альтернативе» все как-то, извините, «сдулось» в один маленький абзац.

                                                                                                        Нет примеров, видно, что автор знает больше, но рассказывать явно не собирается (ссылки — хорошо, но примеры никто не отменял, кстате, некоторые из ссылк на ru-ru msdn, некоторые на en-us). Сухие перечисления без примеров — бесполезны, никто не будет «заучивать» их наизусть, без понимания почему так.

                                                                                                        Почему ?. называют Элвисом (кстате, правильно использовать в качестве термина словосочетание «null-coalescing», а не глалог «coalesce)?

                                                                                                        Почему if(something != null) вдруг „антипаттерн“? „Единственное назначение которого — выбросить исключение поближе к месту предательства“ — никто не выбрасывает NullReferenceException и никто не отменял валидацию параметров метода (InvalidArgumentException) или ошибочных сценариев (InvalidOperationException). Или имелось в виду что-то другое?
                                                                                                          +1
                                                                                                          > Почему?.. называют Элвисом
                                                                                                          Если присмотреться можно увидеть кок и два глаза
                                                                                                            –4
                                                                                                            Элвисом называют ?:

                                                                                                            ?. называет Элвисом только Bonart. Не знаю, почему у него Элвис одноглазый.
                                                                                                              0

                                                                                                              По ссылке ниже тоже только я? Да еще за нескольких участников дискуссии?
                                                                                                              http://stackoverflow.com/questions/27493541/null-conditional-operator-and-string-interpolation-in-c-sharp-6

                                                                                                                0
                                                                                                                По ссылке — только Peter, и ему там в комментариях указали на его заблуждение.
                                                                                                                  0

                                                                                                                  Комментарии от Nick Orlando и Randall Deetz тоже были от меня?
                                                                                                                  И Bill Wagner по ссылке ниже тоже я?
                                                                                                                  http://www.informit.com/articles/article.aspx?p=2421572
                                                                                                                  И гугл при поиске "elvis operator c#" почему-то находит null-conditional operator раньше тернарного.
                                                                                                                  И это все я один?

                                                                                                                    –1
                                                                                                                    Элвис — это не тернарный ?:, а как раз-таки бинарный.
                                                                                                                +1
                                                                                                                По ссылке ниже оператором Элвиса в шарпе называют '??'. При этом указывают, что официально он называется «null-conditional operator», ибо на смайл Элвиса он уже не похож.
                                                                                                                  +1

                                                                                                                  ?? официально называется null-coalescing operator. null-conditional operator — это как раз официальное название для ?.

                                                                                                                    +1
                                                                                                                    Вы это не мне, а процитированному мной человеку напишите. Суть в том, что функциональную роль оператора Элвиса в шарпе выполняет как раз '??'.

                                                                                                                    > null-conditional operator — это как раз официальное название для?..
                                                                                                                    Быть точнее, null-conditional member access operator.
                                                                                                                0
                                                                                                                Где же элвис одноглазый? Привыкли все, что смайлики повёрнуты, как совёнок-брат-дебил. А тут чуб наверху, два глаза внизу:
                                                                                                                ?..
                                                                                                                  –1
                                                                                                                  Ну значит, два разных оператора — это «горизонтальный Элвис» и «вертикальный Элвис».
                                                                                                                  Если верить вики, в Groovy есть они оба.
                                                                                                                +3
                                                                                                                Элвисом называют ?: — тут хорошо виден чуп и глаза, если смотреть на ?. — то и правда неясно — здесь, видимо, «король» нам подмигивает.
                                                                                                                  +4
                                                                                                                  Я посмотрел повнимательнее и теперь и во втором варианте тоже вижу Элвиса! Кажется, я прозрел!1 Привычность восприятия играет с нами шутку. (хинт: точка от вопросительного знака — первый глаз). Только насколько я знаю такой элвис обычно называют safe call и у него немного другой смысл, чем у того элвиса, что повернут как классические смайлы.
                                                                                                                  +3
                                                                                                                  Почему if(something != null) вдруг „антипаттерн“? Единственное назначение которого — выбросить исключение поближе к месту предательства“ — никто не выбрасывает NullReferenceException и никто не отменял валидацию параметров метода (InvalidArgumentException) или ошибочных сценариев (InvalidOperationException).


                                                                                                                  потому что в свете последних за 20 лет веяний в ООП эта задача должна быть переложена на контракты
                                                                                                                    0
                                                                                                                    в «исторической альтернативе» все как-то, извините, «сдулось» в один маленький абзац.

                                                                                                                    А там и не требуется сильно больше: источником серьезной проблемы стала экономия буквально на спичках. Добавил примеры кода к альтернативе.


                                                                                                                    Сухие перечисления без примеров — бесполезны, никто не будет «заучивать» их наизусть, без понимания почему так.

                                                                                                                    В этом случае каждое решение грозит превратиться в отдельную статью, что повысит трудоемкость для меня и ухудшит усвояемость для читателей.
                                                                                                                    Я не стремился подробно осветить каждый нюанс каждого частного случая. Моей задачей было дать обзор типовых проверенных решений и высокоуровневые критерии для их выбора. Для тех, кто хочет узнать больше о конкретном варианте — в тексте есть ссылки.

                                                                                                                    +1
                                                                                                                    Null и вправду великий и предателский, вопрос в другом, что делать если есть таблица 59 миллионов записей,

                                                                                                                    А вот вы говорите что поле не должно быть null. Получается так что для того чтобы добавить поле субд должно будет ставить этому поле значение по умолчанию? В таком случае справится ли СУБД?

                                                                                                                    Больше все го в действительно напрягает что нужно все время делать COALESCE на проверках в СУБД.
                                                                                                                    Но с другой стороны для поля бит мы должны будем инициализировать либо 0 или 1, для поля интегер 0, для пустой строки мы должны будем проинициализировать \0 (нулевым символом). То есть null обусловлен тем что многие субд немогут просто так взять и выкинуть NULL, Потому что при ALTER новый столбец при не NULL порождает дорогостоящую операцию для инициализации 59 миллионов UPDATE.

                                                                                                                    А самая тормозну тая операция в СУБД это как раз таки UPDATE!
                                                                                                                    NULL необходим в СУБД. По крайне мере в RDBMS. И условность это больше техническая нежели концептуальная.
                                                                                                                      0
                                                                                                                      В БД нужно ставить два поля — bit/bool есть значение (результат измерения) и само измерение(int, например), ибо не всегда понятно — 0 это отсутствие измерения или значение измерения 0.
                                                                                                                        +1
                                                                                                                        вообще бит булл, мы понимаем что в теории это один бит, но для того чтобы прочитать бит нужно сначала прочитать байт потом извлечь бит, то как хранится в rdbms разнится для каждой rdbms.

                                                                                                                        И по большей части зависит от того как это реализуется в конкретной СУБД.
                                                                                                                          0
                                                                                                                          Может, не очень удачно выразился — я как раз против излишнего добавления поля (ставить два поля вместо одного — плохо). NULL, конечно, тоже хранится где-то (в виде маски свободных/занятых полей в записи, например), но по крайней мере код объявления, вставки, обновления не растёт от использования NULL, а при втором поле растёт.
                                                                                                                        +1

                                                                                                                        Проблема не в том, есть null или нет, а в том что


                                                                                                                        1. null допускают все типы полей в БД
                                                                                                                        2. Операции с null ведут к ошибкам при исполнении а не при компиляции запроса.

                                                                                                                        Если бы значения integer null и integer not null были бы


                                                                                                                        1. разных типов,
                                                                                                                        2. не совместимых между собой без явного coalesce
                                                                                                                        3. с доступом к большинству операций только для integer not null,

                                                                                                                        то с null и в SQL было бы намного меньше проблем.

                                                                                                                        +1

                                                                                                                        В C++ есть довольно простой способ не писать часть лишних проверок — передача интерфейсов в функцию по ссылке. Тогда внутри самой функции и вызываемых из нее проверки на NULL не нужны (синтаксически), а вызывающему коду приходится явно разыменовать указатель, что для любого программиста красная тряпка и повод железно убедиться, что разыменовываешь не null:


                                                                                                                        void StartVehicle( Vehicle& object )
                                                                                                                        {
                                                                                                                          object.CloseDoors();
                                                                                                                          RunEngine(object); // void RunEngine( Vehicle& object );
                                                                                                                        }
                                                                                                                        void CallerCode()
                                                                                                                        {
                                                                                                                          Vehicle* object = FindObjectSomewhere();
                                                                                                                          if(object) // Проверка только в момент фактического появления неопределенности
                                                                                                                          {
                                                                                                                            StartVehicle(*object);
                                                                                                                          }
                                                                                                                        }
                                                                                                                        

                                                                                                                        Недостатки:


                                                                                                                        • Если нужно хранить объект, то это не всегда возможно сделать в ссылке (например, при отложенной инициализации)
                                                                                                                        • Не для всех привычно использовать перегруженные интерфейсы через ссылку.
                                                                                                                        • Это C++.

                                                                                                                        Пожалуй, в описанном сценарии (передача параметров в функцию) это аналог вашего варианта 2 (NotNull). По-моему, вы его несколько недооценили. Конечно, для полностью нового кода писать везде NotNull (или &) — некоторая морока, но зато на рабочей кодобазе можно потихоньку очищать код, добавляя NotNull снизу вверх, пока указатели с возможно нулевым значением не останутся только на инфраструктурном уровне в местах, где это объективно необходимо.

                                                                                                                          0
                                                                                                                          Есть еще один недостаток — в ваш метод rvalue ссылку вы не передадите, нужно или использовать универсальные ссылки (если это шаблонный код), либо делать ссылку константной, либо дублировать код для rvalue ссылок при необходимости.
                                                                                                                            0

                                                                                                                            Rvalue ссылки имеют смысл, когда объекты передаются по значению (в C# это была бы структура). Это иная парадигма, и там нет проблемы нулевых указателей. Как и при передаче интерфейсов по указателю нет проблемы rvalue ссылок, т.к. указатели тривиально копируются.
                                                                                                                            Кстати, парадигма передачи объектов по значению, если она реализуется последовательно, — тоже, пожалуй, один из ответов на проблему null.


                                                                                                                            Вопрос константности в данном случае продиктован требованиями функции. Для не изменяющей объект функции ссылка, конечно, будет константной.

                                                                                                                          +1
                                                                                                                          грамотный разбор, добавить нечего

                                                                                                                          На предвосхищение ООП через 20 лет не претендую


                                                                                                                          это пять ))
                                                                                                                          • UFO just landed and posted this here
                                                                                                                              +2
                                                                                                                              В императивном подходе избавляться от null нет никакого смысла. Статья написана так густо, что «мессаджа» за ней и не видно. У null-а проблема скорее не в его наличии, а в том, что знание о том может быть значение null-ом или нет содержится в головах, которые пишут код, в хорошем случае в документации и в идеальном в виде аннотации к параметру. Но хотелось бы, чтобы компилятор сам предостерегал нас от проблем с такими значениями. Потому и появляются всякие Optional в таких языках как java, чтобы обозначить, что возвращаемого значения может и не быть и потом что-то с этим удобно сделать.
                                                                                                                              • UFO just landed and posted this here
                                                                                                                                  0
                                                                                                                                  Что делать — это философский вопрос. Это решают создатели языка. В котлине, например, получая из метода значение, тип которого помечен, что там не может быть null, просто нет никакого смысла его проверять на null. А если всё-таки тип возвращенного значения nullable, то ты просто не сможешь вызвать у него метод, пока не проверишь, что это не null. Поначалу кажется сложным, по факту привыкаешь и потом очень без такого страдаешь в java.
                                                                                                                                    0
                                                                                                                                    Ничего плохого в таком подходе пока что не находил.


                                                                                                                                    Куча шаблонного кода?
                                                                                                                                    • UFO just landed and posted this here
                                                                                                                                        +1

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

                                                                                                                                        • UFO just landed and posted this here
                                                                                                                                            0

                                                                                                                                            В котлине есть ссылочные типы, не допускающие null. Им проверки на null не нужны по построению.

                                                                                                                                            • UFO just landed and posted this here
                                                                                                                                                +1

                                                                                                                                                Вы спрашивали, избавит ли в C# от проверок добавка фичи из котлина и каким образом.
                                                                                                                                                Да, от части проверок избавит — ссылочные типы без null соответсвуют решению 12 из статьи и позволяют не проверять их значения на null по построению.
                                                                                                                                                Смысл обсуждать котлин в теме про C# простой:


                                                                                                                                                1. Ссылочные типы без null всегда обсуждаются при разработке новых версий C#.
                                                                                                                                                2. Котлин свободен от NRE, кроме случаев взаимодействия с внешним не-котлин кодом.
                                                                                                                                                3. У JVM и Java проблема с null в точности та же что и в .NET и C#
                                                                                                                                                4. У C# в сравнении с котлином есть дополнительное обременение в виде совместимости с предыдущими версиями языка.
                                                                                                                                                • UFO just landed and posted this here
                                                                                                                                                    +2

                                                                                                                                                    Смысл в том, что по ссылке всегда объект, и с ней можно работать не опасаясь NRE. Если ссылка позволяет null, то компилятор должен выдать ошибку при обращении к объекту, если нет явной проверки на null. Пустой объект не создается, так как корректность использования ссылок проверяется на этапе компиляции.

                                                                                                                                                    • UFO just landed and posted this here
                                                                                                                                                        +2

                                                                                                                                                        Вы можете попробовать читать именно то что написано.


                                                                                                                                                        "Смысл в том, что по ссылке всегда объект" — исходя из Ваших же слов, именно тут и будет создан пустой объект.

                                                                                                                                                        Это не чьи-то слова — это ваши собственные идеи.


                                                                                                                                                        1. Совсем не факт что будет использован паттерн null object — у него есть существенные ограничения.
                                                                                                                                                        2. Когда null object все-таки применяется — нейтральный объект обычно создается статически и переиспользуется многократно.

                                                                                                                                                        Например, метод FindPerson вместо null (всеми почему-то ненавистного) вернет «пустой» объект, потому что null вернуть он не может.

                                                                                                                                                        1. Вы статью вообще прочитали? Недостатки реализации null в C# расписаны еще до ката.
                                                                                                                                                        2. С чего вы взяли что FindPerson должен использовать именно null object? Исключение при неудачном поиске бросить не судьба?
                                                                                                                                                        3. Позволяющих null ссылок заведомо меньше, чем их общее число, а на практике от силы процентов 5. Кроме того, при наличии ссылок без поддержки null проверку для каждого случая появления null достаточно сделать лишь однажды.

                                                                                                                                                        Лично для меня в такой логике ничего полезного нет, проверки на null остались, плюс добавились абсолютно бесполезные «пустые» объекты.

                                                                                                                                                        Вы сами предложили такую логику, не надо приписывать ее кому-то еще.

                                                                                                                                                          0
                                                                                                                                                          1. Чем пустой объект отличается от null? Если фунция должна вернуть объект, она должна вернуть реальный объект или, если не может этого сделать, бросить исключение:


                                                                                                                                                            Person! p =  FindPerson(); // Получаем либо объект, либо исключение PersonNotFound
                                                                                                                                                            Log(p.ToString()); // Работаем с объектом, не опасаясь NRE

                                                                                                                                                            Если функция может вернуть null, то перед использование результата необходима проверка:


                                                                                                                                                            Person p =  FindPersonOrDefault();
                                                                                                                                                            Log(p.ToString()); // должна быть ошибка компиляции, т.к. нет гарантии, что p != null
                                                                                                                                                            if (p != null) {
                                                                                                                                                                Log(p.ToString()); // Работаем с объектом, не опасаясь NRE
                                                                                                                                                            }

                                                                                                                                                          2. Не избавляет, а наоборот, вынуждает писать проверки по месту использования ссылки.


                                                                                                                                                          3. Зачем нужен "пустой" объект и что с ним дальше делать? Проще не создавать.

                                                                                                                                                          P.S. Мы же 12-й пункт обсуждаем?

                                                                                                                                                          • UFO just landed and posted this here
                                                                                                                                                              0
                                                                                                                                                              Прям как на MSDN про винапи: вторым параметров всегда должно идти TRUE, а десятый должен всегда быть NULL. Мы это задокументировали, отлично, раз в доке написано, значит поведение корректное и система хороша.
                                                                                                                                                              по поводу Вашего пункта 2 — выше Bonart написал, что код необходимо избавлять от «кучи шаблонного кода», но с таким подходом шаблонный код никуда не девается.
                                                                                                                                                              Много проверок пишете для проверки int на NULL? Ведь для типов, не содержащих NULL «шаблонный код никуда деться не может».

                                                                                                                                                              Если метод описан, и указано, что он может возвращать null, то в этом нет ничего плохого.

                                                                                                                                                              Не знаю как у вас в 90х, а в 2016 принято кидать эксепшн, а не возвращать код ошибки (в данном случае null это флаг, что нинашла).
                                                                                                                                                              • UFO just landed and posted this here
                                                                                                                                                                  0
                                                                                                                                                                  Потому что есть любители возвращать null ссылочного типа вместо того, чтобы бросить нормальное исключение. Ну вот если бы мы с вами вместе например работали, вы бы наверняка просто вернули бы null (вы же сами это выше и написали), и я бы конечно же сделал проверку на null (ибо фиг знает, что там ваш метод возвращает, лучше подстраховаться).

                                                                                                                                                                  И вот эти лишние проверки, которых вполне не было бы если б нулл вообще не мог прийти занимают по статистике 20% кода (источник не приведу — уже не помню где видел).
                                                                                                                                                                  • UFO just landed and posted this here
                                                                                                                                                                      +3
                                                                                                                                                                      Нормально. Только null не должен быть валидным значением типа. Например int не должен быть null, для возможности вернуть такое значение его явно нужно обернуть в int?.. Что и нужно было изначально сделать для референсных типов.

                                                                                                                                                                      Когда я создаю новый метод, то всегда пишу к нему xml комментарии, где описываю сам метод, его аргументы и возвращаемое значение. И если бы мы с вами работали вместе, то у вас не возникло бы ни единого вопроса (это конечно только если вы читаете описание методов, которые вызываете).

                                                                                                                                                                      Ну то есть так и есть, то что должен делать компилятор вы перекладываете на человека. «Не прочитал XML комментарий — получи эксепшн, так тебе и надо». Почему бы проверкой не заняться компилятору? Ведь если я не прочитал комментарий и не сделал проверку — то я не прав? Почему нет ОБЯЗАТЕЛЬСТВА сделать проверку? Это ведь легко сделать. Например с тем же int? у меня просто нет другого способа передать его в другой метод, который требует число, кроме как вначале проверить на HasValue (ну или свалиться при попытке получить значение).

                                                                                                                                                                      Но конечно же лучше отказаться от всего этого, ведь можно написать xml-комментарий. А еще можно всегда везде возвращать dynamic, а в XML комментарии писать, что за тип на самом деле пришел. Вуух, какая гибкость! А кто не прочитал, тот сам себе злобный буратино.
                                                                                                                                                                      • UFO just landed and posted this here
                                                                                                                                                                          +1
                                                                                                                                                                          «Референсные типы» для того и reference types, что бы не содержать значение, а только на него указывать.
                                                                                                                                                                          Верно, это означает исключительно семантику оператора «присвоить». Соответственно
                                                                                                                                                                          А пока объект ссылочного типа не указывает на значение в куче, то он равен null, по моему это более чем логично.
                                                                                                                                                                          нифига не так. Ссылка всегда должна указывать на существующий объект. Собственно в том же котлине это и сделано, афайк.
                                                                                                                                                                          Это как бы вы выстрелили себе в ногу, и жаловались на то, что револьвер сделан неправильно, ведь это мелочи, что инструкцию вы не прочитали та и вообще использовали его неправильно.

                                                                                                                                                                          Нет, это у вас чуть что не так — пользователь виноват.

                                                                                                                                                                          Вы можете 100 раз ему сказать, что он должен был прочитать документацию, посидеть с бубном и т.п., но если программа сделала не то, что ожидал пользователь — то виноват разработчик. Можете потом сидеть доказывать ему в спину, что он должен был прочитать 200-страничный мануал, а человек просто уйдет к конкурентам, у которых перед тем как дропнуть все данные выводится окошко с подтверждением, а не подход «сам виноват».
                                                                                                                                                                          Кстати, если вам так действительно не хватает данной «фичи», то предложите ее реализовать (или проголосуйте за реализацию, если такая уже есть) для разработчиков самого языка. А то пока вы будете придумывать очередной пример с dynamic, то «зарелизят» следующую версию.
                                                                                                                                                                          Вы за меня не переживайте, такая фича уже заявлена, и я заявку давно заапрувил.

                                                                                                                                                                          Ну и вы не ответили на вопрос, чем плохо переложить на компилятор проверку этих XML-комментариев?
                                                                                                                                                                          0
                                                                                                                                                                          Например int не должен быть null, для возможности вернуть такое значение его явно нужно обернуть в int?.. Что и нужно было изначально сделать для референсных типов
                                                                                                                                                                          Я бы хотел использовать такой язык. Но пока не понимаю, как будет выглядеть default-значение. Например, если Document — не-null тип, то чему равен default(Document)?

                                                                                                                                                                          Например, default(bool)==false, а default(int)==0.
                                                                                                                                                                          Этим значением заполняются поля классов и структур, сразу после их создания.

                                                                                                                                                                          Или not-null ссылки допустимы только в локальных переменных и в возвращаемых из функций значениях?
                                                                                                                                                                            0
                                                                                                                                                                            Обычно просто обязывают инициализировать переменную перед использованием, иначе compile error.
                                                                                                                                                                              0
                                                                                                                                                                              Думаю, если попытаться надёжно покрыть покрыть все сценарии, получим сложную спецификацию, в стиле c++ с его конструкторами копирования и прочими плюшками.

                                                                                                                                                                              Самый простой случай — как должен работать Array.Resize?
                                                                                                                                                                    0
                                                                                                                                                                    У меня в 2016 принято Maybe или Either, а не нарушать систему типов императивными экзепшонами.
                                                                                                                                                                    +1
                                                                                                                                                                    amironov, по поводу Вашего пункта 2 — выше Bonart написал, что код необходимо избавлять от «кучи шаблонного кода», но с таким подходом шаблонный код никуда не девается.

                                                                                                                                                                    Еще раз: когда гарантия not null есть — избавляет от проверок, когда нет — обязывет сделать проверку.

                                                                                                                                                                    • UFO just landed and posted this here
                                                                                                                                                                        +2
                                                                                                                                                                        В таком случае компилятор заставит писать «шаблонный код» if == null, от которого в начале это дискуссии пытались уйти.

                                                                                                                                                                        Цель — избавиться от NRE: чтобы были подсказки от компилятора, когда эти проверки обязательны, а когда нет.


                                                                                                                                                                        Каким образом вы собираетесь избавлять от «шаблонного» кода if == null?

                                                                                                                                                                        void ProcessPerson(Person! person)
                                                                                                                                                                        {
                                                                                                                                                                                // Не надо писать
                                                                                                                                                                                // if (person == null)
                                                                                                                                                                                //     throw new ArgumentNullException("person");
                                                                                                                                                                                Log(p.ToString());
                                                                                                                                                                        }
                                                                                                                                                                        
                                                                                                                                                                        ProcessPerson(new Person()); // ok
                                                                                                                                                                        ProcessPerson(null); // ошибка компиляции
                                                                                                                                                                        • UFO just landed and posted this here
                                                                                                                                                                            +1
                                                                                                                                                                            От if == null вы уходите, только если ссылочный тип не может быть null

                                                                                                                                                                            Совершенно верно. Заодно компилятор даст по рукам, тому кто попытается null туда запихнуть.


                                                                                                                                                                            а если может — вы снова напишите шаблонную проверку.

                                                                                                                                                                            Совершенно верно. Только после первой же успешной проверки у меня будет значение ссылочного типа, не допускающего null и его, в отличие от нынешних, не надо будет проверять по всей глубине стека вызовов. Заодно компилятор даст по рукам тому, кто рискнет пропустить обязательную проверку.


                                                                                                                                                                            Итоги:


                                                                                                                                                                            1. Полностью исключены проверки с выбросом InvalidArgumentException
                                                                                                                                                                            2. А там где null — не ошибка, наличие проверки проконтролирует компилятор.
                                                                                                                                                                            3. NRE исключено по построению
                                                                                                                                                                            4. PROFIT
                                                                                                                                                                              0
                                                                                                                                                                              Никто не говорил, что компилятор решит все проблемы. Смысл в том, чтобы на представленном ниже рисунке оставить только элементы в зеленых областях, потому что красные это либо мусорные проверки (которые не нужны), либо как раз место для NullReference
                                                                                                                                                                              image
                                                                                                                                                                                0
                                                                                                                                                                                Я не понял каким образом разработчики будут вынуждены использовать NOT NULL. Например, найдется человек, который не читает ни xml описания, ни документации (по словам PsyHaSTe это нормально, так как все само должно работать и исправлять ошибки), и вернет по старинке NULL тип вместо NOT NULL. В таком случае компилятор заставит писать «шаблонный код» if == null, от которого в начале это дискуссии пытались уйти.

                                                                                                                                                                                stalsoft не передергивайте. Я говорил про то, что не надо заставлять читать документацию, когда это не является обязательным. А то так действительно дойдем до венгерской нотации, когда все туда-сюда кидается одним и тем же образом, а программист должен сопоставив кучу фактов правильно угадать, что же это такое.
                                                                                                                                                                                • UFO just landed and posted this here