В чем разница между неопределенным в Haskell и нулевым в Java? - PullRequest
50 голосов
/ 19 октября 2010

Оба являются терминами, тип которых является пересечением всех типов (необитаемых).Оба могут быть переданы в коде без сбоев, пока не будет предпринята попытка оценить их.Единственное отличие, которое я вижу, состоит в том, что в Java есть лазейка, которая позволяет оценивать null точно для одной операции, то есть сравнения ссылочного равенства (==), тогда как в Haskell undefined не можетоценивается вообще без исключения.Это единственная разница?

Редактировать

Что я действительно пытаюсь решить с этим вопросом: почему включение null в Java такое явно плохое решение, и какHaskell избежать этого?Мне кажется, что реальная проблема в том, что вы можете сделать что-то полезное с null, а именно вы можете проверить это на nullness .Поскольку вам разрешено это делать, стало общепринятым условием передавать нулевые значения в коде и указывать на них «нет результата» вместо «в этой программе есть логическая ошибка».Принимая во внимание, что в Haskell нет способа проверить, оценивает ли термин дно, не оценивая его и не разрывая программу, поэтому его никогда нельзя было бы использовать таким образом, чтобы указать «нет результата».Вместо этого нужно использовать что-то вроде Maybe.

Извините, если мне кажется, что я играю быстро и свободно с термином "оценивать" ... Я пытаюсь провести здесь аналогию ис трудом формулируя это точно.Я думаю, это признак того, что аналогия неточна.

Ответы [ 2 ]

72 голосов
/ 19 октября 2010

В чем разница между неопределенным в Haskell и нулевым в Java?

Хорошо, давайте вернемся немного назад.

"undefined" в Haskell является примером "нижнего" значения (обозначается ⊥). Такое значение представляет любое неопределенное, застрявшее или частичное состояние в программе.

Существует множество различных форм дна: бесконечные циклы, исключения, сбои сопоставления с образцом - практически любое состояние в программе, которое в некотором смысле не определено. Значение undefined :: a является каноническим примером значения, которое переводит программу в неопределенное состояние.

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

 > undefined = undefined

Или немедленно выйти (старый компилятор Gofer использовал это определение):

 > undefined | False = undefined

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

Зачем вам такая ценность? Ну, на ленивом языке вы часто можете манипулировать структурами или функциями, которые хранят нижние значения, при этом сама программа не является нижней.

например. список бесконечных циклов совершенно сгущен:

 > let xs = [ let f = f in f 
            , let g n = g (n+1) in g 0
            ]
 > :t xs
 xs :: [t]
 > length xs
 2

Я просто мало что могу сделать с элементами списка:

 > head xs
 ^CInterrupted.

Эта манипуляция бесконечными вещами является частью того, почему Хаскелл такой веселый и выразительный. Результатом лени является то, что Haskell уделяет особое внимание значениям bottom.

Однако ясно, что концепция дна одинаково хорошо применима и к Java, или к любому (не тотальному) языку. В Java есть много выражений, которые выдают «нижние» значения:

  • сравнение ссылки с нулем (хотя обратите внимание, но не null, что четко определено);
  • деление на ноль;
  • исключения за пределами;
  • бесконечный цикл и т. Д.

У вас просто нет возможности очень легко заменить одно дно на другое, и компилятор Java мало что делает для обоснования значений дна. Однако такие значения есть.

В итоге

  • разыменование значения null в Java - это одно конкретное выражение, которое возвращает нижнее значение в Java;
  • значение undefined в Haskell - это универсальное нижнее выражение, которое можно использовать везде, где требуется нижнее значение в Haskell.

Вот так они похожи.

Постскриптум

Что касается самого вопроса null: почему это считается дурным тоном?

  • Во-первых, Java null по сути эквивалентен добавлению неявного Maybe a к каждому типу a в Haskell .
  • Разыменование null эквивалентно сопоставлению с образцом только для случая Just: f (Just a) = ... a ...

Таким образом, когда переданное значение равно Nothing (в Haskell) или null (в Java), ваша программа достигает неопределенного состояния. Это плохо: ваша программа вылетает.

Итак, добавив null к каждому типу , вы просто упростили создание значений bottom случайно - типы больше не помогают вам. Ваш язык больше не помогает вам предотвращать ошибки такого рода, и это плохо.

Конечно, все еще существуют другие нижние значения: исключения (например, undefined) или бесконечные циклы. Добавление нового режима возможного сбоя к каждой функции - разыменование null - просто облегчает написание программ, которые аварийно завершают работу.

7 голосов
/ 19 октября 2010

Ваше описание не совсем корректно. Вы говорите, что null не может быть оценено. Однако, поскольку java - нетерпеливый язык, это будет означать, что f(null) сгенерирует NPE независимо от определения f (поскольку аргументы метода всегда оцениваются до его запуска).

Единственная причина, по которой вы можете передать undefined в haskell без исключения, состоит в том, что haskell ленив и не оценивает аргументы без необходимости.

Еще одно отличие между undefined и null заключается в том, что undefined - это простое значение, определенное в стандартной библиотеке. Если он не был определен в стандартной библиотеке, вы можете определить его самостоятельно (например, написав myUndefined = error "My Undefined).

В Java null - это ключевое слово. Если бы не было ключевого слова null, вы бы не смогли его определить (выполнение эквивалента определения haskell, т. Е. Object myNull = throw(new Exception()), не сработало бы, потому что выражение было бы оценено прямо там).

...