Como nada é um subtipo de qualquer outro tipo no Scala

19

Estou fazendo o curso coursera de Martin Odersky sobre programação funcional com scala e, por enquanto, aprendi duas coisas que juntas não fazem sentido:

  1. Scala não suporta herança múltipla
  2. Nothing é um subtipo de qualquer outro tipo

Essas duas declarações não podem viver juntas, então como exatamente isso é feito? e qual é exatamente o significado de "subtipo de qualquer outro tipo"

Editar 1

Na API Scala , Nothingé definido como abstract final class Nothing extends Any... então, como ele pode estender outras classes?

vainolo
fonte
Esta página pode ajudar um pouco: artima.com/pins1ed/scalas-hierarchy.html
jhewlett
Tanto quanto eu posso ver isso é definido como "final traço Nada se estende Qualquer" scala-lang.org/api/2.7.6/scala/Nothing.html
Den
8
Você está confundindo tipos e classes. Esses dois são coisas muito diferentes. Infelizmente, você não é o único que está confuso com essa distinção, e realmente infelizmente, alguns daqueles que são confundidos acontecem ser os projetistas de linguagens populares como Java, C # e C ++. Não diz que Nothingé uma subclasse de todas as outras classes. Diz que é um subtipo de qualquer outro tipo .
Jörg W Mittag
1
@ delnan: As interfaces do Java são obtidas diretamente dos protocolos do Smalltalk. No Smalltalk, apenas protocolos são tipos, classes não. Em Java, ambas as interfaces e classes são tipos. Isto é errado. Classes não são tipos, apenas interfaces são. O fato de todas essas linguagens terem coisas que são tipos e não classes é irrelevante. O problema é que nessas línguas as classes são tipos, o que está errado.
Jörg W Mittag
1
@ JörgWMittag Essa é uma afirmação diferente e extremamente discutível (tendo a concordar que é prejudicial, mas não atribuiria isso a um mal-entendido de digitação). Não faz sentido discutir aqui.

Respostas:

27

Subtipagem e herança são duas coisas diferentes! Nothingnão estende tudo, é um subtipo , apenas se estende Any.

A especificação [§3.5.2] possui um caso especial que rege a relação de subtipagem de Nothing:

§3.5.2 Conformidade

  • [...]
  • Para cada tipo de valor
    T,scala.Nothing <: T <:scala.Any
  • Para cada construtor de tipo T(com qualquer número de parâmetros de tipo)
    scala.Nothing <: T <: scala.Any
  • [...]

Onde <:basicamente significa "é um subtipo de".

Quanto a como isso é feito: não sabemos, é mágica do compilador e um detalhe de implementação.

Muitas vezes, uma linguagem faz coisas que você, como programador, não pode. Como contrapartida de Nothing: tudo em Scala herda Any, tudo exceto Any . Por que não Anyherda de algo? Você não pode fazer isso. Por que Scala pode fazer isso? Bem, porque Scala estabeleceu as regras, não você. Nothingser um subtipo de tudo é apenas outra instância disso.

phant0m
fonte
10
BTW: isso é exatamente o mesmo que nullser atribuível a um campo de todo tipo em Java. Por que isso é possível? É nulluma instância de toda classe? Não, é possível porque o compilador diz isso. Período.
Jörg W Mittag
8
Se eu pudesse votar isso cem vezes, eu faria. Tipos e classes confusos é uma das piores coisas que linguagens como Java nos trouxeram.
Jörg W Mittag
1
Para as almas curiosas sobre diferença entre herança e subtipos cmi.ac.in/~madhavan/courses/pl2006/lecturenotes/lecture-notes/... porém eu não comprá-lo - se você herdar (como extendsem Java, e não como compor ) você faz isso para subtipagem, afinal.
greenoldman
11

Quando ele diz que Scala não suporta herança múltipla, ele se refere à herança de uma implementação de método várias vezes. Obviamente, você pode implementar várias interfaces / características em uma classe e elas podem até definir o mesmo método, mas não há conflito entre as diferentes implementações devido à linearização de características.

Em geral, se você tem uma classe C1com um método f()e uma classe C2também com um método f(), a herança múltipla significa que você pode, de alguma forma, herdar as duas implementações de f(). Isso pode levar a vários problemas, que o Scala resolve ao permitir que você herde apenas de uma única classe e, no caso de várias características, selecionando uma implementação com base na ordem das características.

Quanto às Nothingcoisas, são realmente simples, porque nada não possui atributos ou métodos definidos. Portanto, você não pode ter nenhum conflito de herança. Mas suponho que a maior parte da sua surpresa venha de um entendimento diferente da herança múltipla.

Depois de entender que a linearização de características elimina efetivamente qualquer ambiguidade da herança, e que não nos referimos à herança de múltiplas características como herança múltipla devido a isso, você deve ficar bem.

Quanto à forma como isso é realizado: o compilador é eventualmente responsável por isso. Consulte a seção 3.5.2 da especificação de idioma Scala , que entre outras propriedades inclui:

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

Ou, em outras palavras, se você deseja implementar um compilador corretamente, ele deve ser manipulado Nothingcomo um subtipo de tudo por especificação. Por razões óbvias, Nothingnão está definido para estender-se a todas as classes carregadas no sistema, mas a relevância de definir Nothingcomo subtipo é limitada a todos os locais onde a subtipagem é relevante.

Um ponto importante aqui é que não existe instância de tipo Nothing, portanto, seu tratamento é estritamente limitado à verificação de tipo, que é tudo no domínio do compilador.

Frank
fonte
2
O que eu ainda não entendo é como isso é feito ... Ver a edição à minha pergunta
vainolo
1
"a relevância de definir Nada como subtipo é limitada a todos os lugares, onde a subtipagem é relevante." O que você quer transmitir com isso? X é relevante onde X é relevante?
Phant0m