Come è niente un sottotipo di ogni altro tipo in Scala


19

Sto seguendo il corso di Martin Odersky sulla programmazione funzionale con scala, e per ora ho imparato due cose che insieme non hanno senso:

  1. Scala non supporta l'ereditarietà multipla
  2. Nothing è un sottotipo di ogni altro tipo

Queste due affermazioni non possono convivere, quindi come si fa esattamente? e qual è esattamente il significato di "sottotipo di ogni altro tipo"

Modifica 1

Nel API Scala , Nothingè definito come abstract final class Nothing extends Any... così come può estendere altre classi?


Questa pagina potrebbe essere di aiuto: artima.com/pins1ed/scalas-hierarchy.html
jhewlett

Per quanto posso vedere, è definito come "tratto finale Nulla si estende qualsiasi" scala-lang.org/api/2.7.6/scala/Nothing.html
Den

8
Stai confondendo tipi e classi. Queste due cose sono molto diverse. Sfortunatamente, non sei l'unico ad essere confuso da questa distinzione, e davvero sfortunatamente, alcuni di quelli che sono confusi sono i progettisti di linguaggi popolari come Java, C # e C ++. Non dice che Nothingsia una sottoclasse di ogni altra classe. Dice che è un sottotipo di ogni altro tipo .
Jörg W Mittag,

1
@delnan: le interfacce Java sono prese direttamente dai protocolli di Smalltalk. In Smalltalk, solo i protocolli sono tipi, le classi no. In Java, entrambe le interfacce e le classi sono tipi. È sbagliato. Le classi non sono tipi, solo le interfacce lo sono. Il fatto che tutte queste lingue abbiano cose che sono tipi e non classi è irrilevante. Il problema è che in quelle lingue le classi sono tipi, il che è sbagliato.
Jörg W Mittag,

1
@ JörgWMittag È un'affermazione diversa ed estremamente discutibile (tendo a concordare sul fatto che sia dannoso, ma non lo attribuirei a un malinteso di battitura). Inutile discuterne qui.

Risposte:


27

Sottotipizzazione ed eredità sono due cose diverse! Nothingnon estende tutto, è un sottotipo , si estende solo Any.

La specifica [§3.5.2] ha un caso speciale che regola la relazione di sottotipizzazione di Nothing:

§3.5.2 Conformità

  • [...]
  • Per ogni tipo di valore
    T,scala.Nothing <: T <:scala.Any
  • Per ogni costruttore di tipi T(con qualsiasi numero di parametri di tipo)
    scala.Nothing <: T <: scala.Any
  • [...]

Dove <:sostanzialmente significa "è un sottotipo di".

Per quanto riguarda il modo in cui ciò viene fatto: non lo sappiamo, è la magia del compilatore e un dettaglio di implementazione.

Molto spesso una lingua fa cose che un programmatore non può fare. In contropartita a Nothing: Tutto in Scala eredita Any, tutto tranne Any . Perché non Anyeredita da qualcosa? Non puoi farlo. Perché Scala può farlo? Bene, perché Scala ha fissato le regole, non tu. Nothingessere un sottotipo di tutto è solo un'altra istanza di questo.


10
A proposito: questo è esattamente lo stesso nullassegnabile a un campo di ogni tipo in Java. Perché è possibile? È nullun'istanza di ogni classe? No, è possibile perché lo dice il compilatore. Periodo.
Jörg W Mittag,

8
Se potessi votare questo cento volte, lo farei. Tipi e classi confusi sono una delle cose peggiori che linguaggi come Java ci hanno portato.
Jörg W Mittag,

1
Per le anime curiose sulla differenza tra eredità e sottotipi cmi.ac.in/~madhavan/courses/pl2006/lecturenotes/lecture-notes/… tuttavia non lo compro - se erediti (come extendsin Java e non come componi ) lo fai per il sottotipo dopo tutto.
Greenoldman,

11

Quando afferma che Scala non supporta l'ereditarietà multipla, si riferisce all'ereditarietà dell'implementazione di un metodo più volte. Naturalmente, è possibile implementare più interfacce / tratti in una classe e possono persino definire lo stesso metodo, ma non si crea un conflitto tra le diverse implementazioni a causa della linearizzazione dei tratti.

In generale, se hai una classe C1con un metodo f()e una classe C2anche con un metodo f(), l'ereditarietà multipla significa che puoi in qualche modo ereditare entrambe le implementazioni di f(). Questo può portare a vari problemi, che Scala risolve solo lasciandoti ereditare da una singola classe e in caso di tratti multipli selezionando una implementazione in base all'ordine dei tratti.

Per quanto riguarda le Nothingcose sono davvero semplici, perché nulla non ha attributi o metodi definiti. Quindi non puoi avere alcun conflitto ereditario. Ma presumo che la maggior parte della tua sorpresa provenga da una diversa comprensione dell'eredità multipla.

Una volta compreso che la linearizzazione dei tratti elimina effettivamente qualsiasi ambiguità dell'eredità e che non ci riferiamo all'ereditarietà da più tratti come eredità multipla a causa di ciò, allora dovresti andare bene.

Quanto a come questo viene realizzato: il compilatore è alla fine responsabile di questo. Vedere la conformità alla sezione 3.5.2 delle specifiche del linguaggio Scala , che tra le altre proprietà include:

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

O in altre parole, se si desidera implementare correttamente un compilatore, deve gestirlo Nothingcome sottotipo di tutto in base alle specifiche. Per ovvie ragioni, Nothingnon è definito per estendersi da tutte le classi caricate nel sistema, ma la rilevanza della definizione Nothingcome sottotipo è limitata a tutti i luoghi, dove il sottotipo è rilevante.

Un punto importante qui è che non esiste alcuna istanza di tipo Nothing, quindi il suo trattamento è strettamente limitato al controllo del tipo, che è tutto nel regno del compilatore.


2
Quello che ancora non capisco è come questo è fatto ... Vedi la modifica alla mia domanda
vainolo

1
"la pertinenza della definizione di Nothing come sottotipo è limitata a tutti i luoghi in cui il sottotipo è rilevante." Cosa vuoi comunicare con quello? X è rilevante dove X è rilevante?
phant0m
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.