Использование Null / Nothing / Unit в Scala

95

Я только что прочитал: http://oldfashionedsoftware.com/2008/08/20/a-post-about-nothing/

Насколько я понимаю, Nullэто трейт и единственный его экземпляр null.

Когда метод принимает аргумент Null, мы можем передать ему только Nullссылку или nullнапрямую, но не любую другую ссылку, даже если она имеет значение NULL ( nullString: String = nullнапример).

Мне просто интересно, в каких случаях использование этой Nullчерты может быть полезно. Также есть черта Nothing, для которой я больше не вижу примеров.


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


Есть ли у вас использование Unit / Null / Nothing как чего-то другого, кроме возвращаемого типа?

Себастьян Лорбер
источник

Ответы:

80

Вы используете Nothing только в том случае, если метод никогда не возвращается (это означает, что он не может нормально завершиться путем возврата, он может вызвать исключение). Ничто никогда не инстанцируется и используется на благо системы типов (процитирую Джеймса Айри: «Причина, по которой Scala имеет нижний тип, связана с его способностью выражать вариативность в параметрах типа» ). Из статьи, на которую вы ссылаетесь:

Еще одно использование Nothing - это тип возвращаемого значения для методов, которые никогда не возвращаются. Если задуматься, это имеет смысл. Если тип возвращаемого значения метода - Nothing и не существует абсолютно никакого экземпляра Nothing, то такой метод никогда не должен возвращать.

Ваш метод ведения журнала вернет Unit. Есть значение Unit, поэтому его можно вернуть. Из документации API :

Unit - это подтип scala.AnyVal. Существует только одно значение типа Unit, (), и оно не представлено никаким объектом в базовой системе времени выполнения. Метод с возвращаемым типом Unit аналогичен методу Java, который объявлен как void.

Натан Хьюз
источник
2
Спасибо, под «никогда не возвращается» вы имеете в виду, что вызов блокируется на неопределенный срок (например, метод запуска планировщика заданий?)
Себастьен Лорбер,
3
@Sabastien: он не возвращается нормально, он может вызвать исключение (см. James-iry.blogspot.com/2009/08/… ). если блокирующий вызов заканчивается только выдачей исключения, это будет засчитываться. спасибо за вопрос, это требует разъяснения.
Натан Хьюз
18

Статья, которую вы цитируете, может ввести в заблуждение. NullТипа для совместимости с виртуальной машиной Java и Java , в частности.

Мы должны учитывать, что Scala :

  • полностью объектно-ориентированный: каждое значение является объектом
  • строго типизирован: каждое значение должно иметь тип
  • необходимо обрабатывать nullссылки для доступа, например, к библиотекам и коду Java

таким образом, возникает необходимость определить тип для nullзначения, которое является Nullпризнаком и имеет nullего единственный экземпляр.

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

pagoda_5b
источник
это правда, так что, в конце концов, нет другого использования Null?
Себастьен Лорбер
@SebastienLorber отредактировал ответ. На самом деле я не вижу никакого использования для среднего разработчика. Может, кто-нибудь еще придумает что-нибудь полезное.
pagoda_5b
Спасибо за это. Если мы знаем причину такого рода вещей, мы понимаем их, в противном случае мы их помним.
Sreekar
Null полезен, когда у вас есть параметр типа и вы можете захотеть вернуть null, как в этом вопросе и ответе , поскольку вы отговариваетесь от использования null в scala, он редко возникает, хотя в системе типов могут быть и другие применения
Даниэль Карлссон
15

Есть ли у вас использование Unit / Null / Nothing как чего-то другого, кроме возвращаемого типа?


Unit можно использовать так:

def execute(code: => Unit):Unit = {
  // do something before
  code
  // do something after
}

Это позволяет вам передать произвольный блок кода для выполнения.


Nullможет использоваться как нижний тип для любого значения, допускающего значение NULL. Пример такой:

implicit def zeroNull[B >: Null] =
    new Zero[B] { def apply = null }

Nothing используется в определении None

object None extends Option[Nothing]

Это позволяет вам назначать a Noneлюбому типу, Optionпотому что все Nothing«расширяет».

val x:Option[String] = None
EECOLOR
источник
Хорошо. Для использования Unit вы могли бы использовать общий тип, чтобы выполнение могло возвращать этот общий тип, если ваш блок кода возвращает что-то еще, кроме unit.
Себастьен Лорбер
Как сказал @drexin в комментарии к другому ответу, он в основном используется для обозначения побочного эффекта.
EECOLOR
6

если вы используете Nothing, делать нечего (включая консоль печати), если вы что-то делаете, используйте тип выводаUnit

object Run extends App {
  //def sayHello(): Nothing = println("hello?")
  def sayHello(): Unit = println("hello?")
  sayHello()
}

... тогда как пользоваться Nothing?

trait Option[E]
case class Some[E](value: E) extends Option[E]
case object None extends Option[Nothing]
Curycu
источник
1
Кроме того, Ein Optionдолжен быть в ковариантном положении: trait Option[+E]чтобы разрешить такие вещи, какval x: Option[Int] = None
vim
5

Я никогда не использовал этот Nullтип, но вы его используете там Unit, где вы бы использовали java void. Nothing- это особый тип, потому что, как уже упоминал Натан, не может быть экземпляра Nothing. Nothingэто так называемый нижний тип, что означает, что это подтип любого другого типа. Это (и параметр контравариантного типа) - вот почему вы можете добавить любое значение к Nil- а это - List[Nothing]- и тогда список будет иметь этот тип элементов. Noneтакже если типа Option[Nothing]. Каждая попытка получить доступ к значениям внутри такого контейнера вызовет исключение, потому что это единственный допустимый способ возврата из метода типа Nothing.

дрексин
источник
спасибо, я не знал, что None расширяет Option [Nothing]. Это имеет смысл в использовании некоторых обобщений, когда подтип может использовать Nothing (я думаю, трудно найти пример для Null и Unit ...)
Себастьян Лорбер
Модуль используется там, где возникают побочные эффекты, например, монада ввода-вывода может быть типа IO[Unit]для печати на консоли и т.п.
drexin
Да, никогда не использовал монаду ввода-вывода, имеет смысл использовать ее с модулем (и, возможно, с Nothing, если это какая-то операция ввода-вывода, которая производит бесконечный поток?)
Себастьен Лорбер
Нет, в этом нет ничего лишнего.
drexin
3

Часто ничего не используется неявно. В приведенном ниже коде, другой пункт имеет тип Ничего , что подкласс Boolean (равно как и любой другой AnyVal). Таким образом, все присвоение действительно для компилятора, хотя предложение else на самом деле ничего не возвращает.val b: Boolean = if (1 > 2) false else throw new RuntimeException("error")

Фанг Чжан
источник
1

Вот пример Nothingиз scala.predef:

  def ??? : Nothing = throw new NotImplementedError

Если вы не знакомы (и поисковые системы не могут искать по нему), ???это функция-заполнитель Scala для всего, что еще не было реализовано. Прямо как у Котлина TODO.

Вы можете использовать тот же трюк при создании фиктивных объектов: переопределите неиспользуемые методы пользовательским notUsedметодом. Преимущество неиспользования в ???том, что вы не получите предупреждений о компиляции вещей, которые никогда не собираетесь реализовывать.

Дэвид Леппик
источник
0

С точки зрения теории категорий « Ничто» является исходным объектом, а Единица - конечным объектом .

https://en.wikipedia.org/wiki/Initial_and_terminal_objects

Начальные объекты также называются котерминальными или универсальными , а конечные объекты также называются конечными .

Если объект одновременно является начальным и конечным , он называется нулевым объектом или нулевым объектом.

Джо
источник