Как Ничто не является подтипом любого другого типа в Scala

19

Я учусь на курсе Мартина Одерского по функциональному программированию в Scala, и сейчас я изучил две вещи, которые вместе не имеют смысла:

  1. Scala не поддерживает множественное наследование
  2. Nothing это подтип любого другого типа

Эти два утверждения не могут жить вместе, так как именно это делается? и что именно означает «подтип любого другого типа»

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

В API Scala , Nothingопределяется как abstract final class Nothing extends Any... так как он может распространяться на другие классы?

vainolo
источник
Эта страница может немного помочь: artima.com/pins1ed/scalas-hierarchy.html
jhewlett
Насколько я могу видеть , что это определяется как «конечная черта Ничто не проходит Любой» scala-lang.org/api/2.7.6/scala/Nothing.html
Den
8
Вы путаете типы и классы. Эти два очень разные вещи. К сожалению, вы не единственный, кто смущен этого различия, и в самом деле , к сожалению, некоторые из тех , кого смущает случается конструкторы популярных языков , таких как Java, C # и C ++. Это не говорит о том, что Nothingэто подкласс любого другого класса. Это говорит о том, что это подтип любого другого типа .
Йорг Миттаг
1
@delnan: интерфейсы Java взяты непосредственно из протоколов Smalltalk. В Smalltalk только протоколы являются типами, а классы - нет. В Java и интерфейсы, и классы являются типами. Это неверно. Классы не являются типами, только интерфейсы. Тот факт, что все эти языки имеют типы, а не классы, не имеет значения. Проблема в том, что в этих языках классы являются типами, что неправильно.
Йорг Миттаг
1
@ JörgWMittag Это другое утверждение, и его можно спорить (я склонен согласиться с тем, что это вредно, но я бы не стал приписывать это неправильному пониманию при наборе текста). Нет смысла обсуждать это здесь.

Ответы:

27

Подтип и наследование - это две разные вещи! Nothingне расширяет все, это подтип , он только расширяется Any.

Спецификация [§3.5.2] имеет особый случай , регулирующий подтипирования-отношения Nothing:

§3.5.2 Соответствие

  • [...]
  • Для каждого типа значения
    T,scala.Nothing <: T <:scala.Any
  • Для каждого конструктора типа T(с любым количеством параметров типа)
    scala.Nothing <: T <: scala.Any
  • [...]

Где в <:основном означает «это подтип».

Что касается того, как это сделать: мы не знаем, это магия компилятора и детали реализации.

Довольно часто язык делает то, что вы, как программист, не можете. Как аналог Nothing: все в Scala наследуется Any, все, кроме Any . Почему не Anyнаследует от чего-то? Вы не можете сделать это. Почему Scala может это сделать? Ну, потому что Скала устанавливает правила, а не ты. NothingБыть подтипом всего лишь другой пример этого.

phant0m
источник
10
Кстати: это то же самое, nullчто присваиваться полю любого типа в Java. Почему это возможно? Является nullли экземпляр каждого класса? Нет, это возможно, потому что так говорит компилятор. Период.
Йорг Миттаг
8
Если бы я мог это повторить сто раз, я бы это сделал. Запутанные типы и классы - это одна из худших вещей, которые языки, такие как Java, навлекли на нас.
Йорг Миттаг
1
Для любопытных душ о разнице между наследованием и подтипы cmi.ac.in/~madhavan/courses/pl2006/lecturenotes/lecture-notes/... однако я не купить его , - если вы унаследовали (например , extendsв Java, а не композе Вы делаете это для подтипирования в конце концов.
Гринольдман
11

Когда он говорит, что Scala не поддерживает множественное наследование, он ссылается на наследование реализации метода несколько раз. Конечно, вы можете реализовать несколько интерфейсов / признаков в классе, и они могут даже определять один и тот же метод, но вы не получите конфликта между различными реализациями из-за линеаризации признаков.

В общем, если у вас есть класс C1с методом f()и класс C2с методом f(), то множественное наследование означает, что вы можете каким-то образом наследовать обе реализации f(). Это может привести к различным проблемам, которые Scala решает, позволяя вам наследовать только от одного класса, а в случае нескольких признаков - выбирать одну реализацию на основе порядка признаков.

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

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

Что касается того, как это реализовано: компилятор в конечном итоге несет ответственность за это. См. Соответствие спецификации языка Scala в разделе 3.5.2, которое среди прочих свойств включает в себя:

For every type constructor T (with any number of type parameters), scala.Nothing <: T <: scala.Any.

Или, другими словами, если вы хотите правильно реализовать компилятор, он должен обрабатывать Nothingкак подтип все по спецификации. По очевидным причинам Nothingне определено расширение из всех классов, загруженных в систему, но актуальность определения в Nothingкачестве подтипа ограничена всеми местами, где подтип является релевантным.

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

Фрэнк
источник
2
То, что я до сих пор не понимаю, как это делается ... См.
Правку
1
«Актуальность определения Ничто как подтипа ограничена всеми местами, где подтип актуален». Что вы хотите передать с этим? X имеет отношение, где X имеет отношение?
phant0m