¿Cómo es Nothing un subtipo de cualquier otro tipo en Scala?

19

Estoy tomando el curso de Martin Odersky sobre programación funcional con scala, y por ahora he aprendido dos cosas que juntas no tienen sentido:

  1. Scala no admite herencia múltiple
  2. Nothing es un subtipo de cualquier otro tipo

Estas dos declaraciones no pueden vivir juntas, entonces, ¿cómo se hace esto exactamente? y cuál es exactamente el significado de "subtipo de cualquier otro tipo"

Editar 1

En la API de Scala , Nothingse define como abstract final class Nothing extends Any... entonces, ¿cómo puede extender otras clases?

vainolo
fuente
Esta página puede ayudar un poco: artima.com/pins1ed/scalas-hierarchy.html
jhewlett
Por lo que puedo ver, se define como "rasgo final Nada se extiende" scala-lang.org/api/2.7.6/scala/Nothing.html
Den
8
Estás confundiendo tipos y clases. Esos dos son cosas muy diferentes. Desafortunadamente, no eres el único que está confundido por esa distinción, y realmente desafortunadamente, algunos de los que están confundidos resultan ser los diseñadores de lenguajes populares como Java, C # y C ++. No dice que Nothingsea ​​una subclase de todas las demás clases. Dice que es un subtipo de cualquier otro tipo .
Jörg W Mittag el
1
@delnan: las interfaces de Java se toman directamente de los protocolos de Smalltalk. En Smalltalk, solo los protocolos son tipos, las clases no. En Java, ambas interfaces y clases son tipos. Eso está mal. Las clases no son tipos, solo las interfaces lo son. El hecho de que todos estos lenguajes tengan cosas que son tipos y no clases es irrelevante. El problema es que en esos idiomas las clases son tipos, lo cual está mal.
Jörg W Mittag el
1
@ JörgWMittag Esa es una declaración diferente y extremadamente discutible (tiendo a estar de acuerdo en que es dañino, pero no atribuiría esto a un malentendido sobre la escritura). No tiene sentido discutirlo aquí.

Respuestas:

27

¡El subtipo y la herencia son dos cosas diferentes! Nothingno extiende todo, es un subtipo , solo se extiende Any.

La especificación [§3.5.2] tiene un caso especial que rige la relación de subtipo de Nothing:

§3.5.2 Conformidad

  • [...]
  • Para cada tipo de valor
    T,scala.Nothing <: T <:scala.Any
  • Para cada constructor de tipos T(con cualquier número de parámetros de tipo)
    scala.Nothing <: T <: scala.Any
  • [...]

Donde <:básicamente significa "es un subtipo de".

En cuanto a cómo se hace esto: no lo sabemos, es magia del compilador y un detalle de implementación.

Muy a menudo un lenguaje hace cosas que usted como programador no puede hacer. Como contrapartida de Nothing: Todo en Scala hereda de Any, todo excepto Any . ¿Por qué no Anyhereda de algo? No puedes hacer eso. ¿Por qué Scala puede hacer eso? Bueno, porque Scala estableció las reglas, no tú. Nothingser un subtipo de todo es solo otra instancia de esto.

phant0m
fuente
10
Por cierto: esto es exactamente lo mismo que nullser asignable a un campo de todo tipo en Java. ¿Por qué es eso posible? ¿Es nulluna instancia de cada clase? No, es posible porque el compilador lo dice. Período.
Jörg W Mittag el
8
Si pudiera votar esto cien veces, lo haría. Tipos y clases confusos es una de las peores cosas que los lenguajes como Java nos han traído.
Jörg W Mittag el
1
Para almas curiosas acerca de la diferencia entre herencia y subtipos cmi.ac.in/~madhavan/courses/pl2006/lecturenotes/lecture-notes/... sin embargo, no lo compro, si heredas (como extendsen Java, y no como componer ) lo haces para subtipar después de todo.
greenoldman
11

Cuando dice que Scala no admite la herencia múltiple, se refiere a heredar la implementación de un método varias veces. Por supuesto, puede implementar múltiples interfaces / rasgos en una clase, e incluso pueden definir el mismo método, pero no obtiene un conflicto entre las diferentes implementaciones debido a la linealización de rasgos.

En general, si tiene una clase C1con un método f()y una clase C2también con un método f(), la herencia múltiple significa que de alguna manera puede heredar ambas implementaciones de f(). Esto puede conducir a varios problemas, que Scala resuelve al solo dejarle heredar de una sola clase y en el caso de múltiples rasgos seleccionando una implementación basada en el orden de los rasgos.

En cuanto a las Nothingcosas, son realmente simples, porque nada tiene atributos o métodos definidos. Por lo tanto, no puede tener ningún conflicto de herencia. Pero supongo que la mayor parte de su sorpresa proviene de una comprensión diferente de la herencia múltiple.

Una vez que comprenda que la linealización de rasgos elimina efectivamente cualquier ambigüedad de la herencia, y que no nos referimos a heredar de múltiples rasgos como herencia múltiple debido a eso, entonces debería estar bien.

En cuanto a cómo se realiza esto: el compilador finalmente es responsable de esto. Consulte la sección de especificación del lenguaje Scala 3.5.2 conformidad, que entre otras propiedades incluye:

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

O, en otras palabras, si desea implementar un compilador correctamente, debe manejarse Nothingcomo un subtipo de todo por especificación. Por razones obvias, Nothingno está definido para extenderse desde todas las clases cargadas en el sistema, pero la relevancia de definir Nothingcomo subtipo está limitada a todos los lugares, donde el subtipo es relevante.

Un punto importante aquí es que no existe una instancia de tipo Nothing, por lo tanto, su tratamiento se limita estrictamente a la verificación de tipo, que está en el ámbito del compilador.

Franco
fuente
2
Lo que todavía no entiendo es cómo se hace esto ... Vea la edición de mi pregunta
vainolo
1
"La relevancia de definir Nothing como subtipo se limita a todos los lugares, donde el subtipo es relevante". ¿Qué quieres transmitir con eso? X es relevante donde X es relevante?
phant0m