Por que o compilador Scala não pode dar um aviso de correspondência de padrões para classes / características não seladas?


10

Se eu usar um un selado traitou abstract classno Scala e depois usar a correspondência de padrões, será que o compilador não sabe em tempo de compilação para essa correspondência de padrão específica que implementações possíveis dessa característica / classe estão disponíveis? Portanto, se isso acontecer, não poderia dar avisos de correspondência de padrão, mesmo que o trait/ abstract classnão esteja selado porque ele sabe quais tipos podem ser usados, verificando todas as dependências / importações possíveis?

Por exemplo, se eu tenho uma Option[A]correspondência de padrões e faço apenas para Some[A]mas não para None, o compilador irá reclamar, porque Optionestá selado.

Se o compilador não pode saber / resolver isso, por que não pode? E se o compilador (teoricamente) puder fazer isso, quais são as razões para isso não ser usado no Scala? Existem outros idiomas que suportam esse tipo de comportamento?


Não está claro o que você está perguntando. Deseja que o compilador emita um aviso se a expressão de correspondência não cobrir todas as entradas possíveis? Talvez um exemplo torne sua pergunta mais clara.
kdgregory

2
Se alguém puder introduzir uma nova subclasse, a correspondência de padrões nunca poderá ser exaustiva. Por exemplo, você produzir alguma classe abstrata Foocom subclasses A, Be C, e todos os seus jogos padrão de correspondência apenas aqueles três. Nada me impede de adicionar uma nova subclasse Dque exploda suas correspondências de padrões.
Doval

@kdgregory Sim, você conseguiu. Eu adicionei um exemplo para deixar mais claro.
valenterry

3
A verificação de todas as importações não é suficiente para descobrir todas as subclasses possíveis. Outra subclasse pode ser declarada em um arquivo de classe separado que é carregado posteriormente durante o tempo de execução via java.lang.ClassLoader.
amon

Respostas:


17

Descobrir todas as subclasses de uma classe é chamado Análise de Hierarquia de Classes, e executar CHA estático em um idioma com carregamento dinâmico de código é equivalente a resolver o Problema da Parada.

Além disso, um dos objetivos do Scala é a compilação e a implantação separadas de módulos independentes; portanto, o compilador simplesmente não pode saber se uma classe está ou não subclassificada em outro módulo, porque nunca analisa mais de um módulo. (Afinal, você pode compilar um módulo na interface de algum outro módulo sem que esse módulo exista no seu sistema!) É por isso que sealedexige que todas as subclasses sejam definidas na mesma unidade de compilação.

Essa também é uma das razões pelas quais as JVMs podem competir tão favoravelmente com os compiladores C ++: os compiladores C ++ são tipicamente compiladores estáticos; portanto, eles geralmente não conseguem descobrir se um método é substituído ou não e, portanto, não podem ser incorporados. As JVMs OTOH, geralmente são compiladores dinâmicos, não precisam executar o CHA para descobrir se um método é substituído ou não, podem apenas olhar para a Hierarquia de Classes em tempo de execução. E mesmo que, posteriormente, na execução do programa, ocorra uma nova subclasse que não existia antes, não é grande coisa, apenas recompile esse trecho de código sem especificar.

Nota: tudo isso se aplica apenas ao Scala. A JVM não tem noção de sealed, portanto, é perfeitamente possível subclassificar sealedclasses de outro idioma da JVM, pois não há como comunicar isso para outro idioma. A sealedpropriedade é registrada na ScalaSiganotação, mas os compiladores de outros idiomas não levam em conta essas anotações, obviamente.


3

Isso pode ser feito (pelo menos para todas as classes conhecidas em tempo de compilação), é apenas caro. Você destruiria completamente a compilação incremental, porque tudo o que contém uma correspondência de padrões precisaria ser recompilado toda vez que qualquer outro arquivo fosse alterado.

E o que você está comprando? É um cheiro de código para escrever correspondências de padrões que precisam mudar frequentemente quando uma nova classe derivada é adicionada. É uma violação do princípio aberto / fechado . Use a herança corretamente e você não precisará escrever esses tipos de correspondências de padrões. E sim, o princípio aberto / fechado também se aplica a linguagens funcionais sem herança baseada em classe. De fato, entre recursos como classes de tipos, métodos múltiplos e funções simples de ordem superior, as linguagens funcionais tornam a extensão sem modificação muito mais fácil.


1
It can be done (at least for all classes known at compile time), it's just expensive.Mas se o programa não for 100% independente (ou seja, depende de .jararquivos externos ), você não poderá entrar em uma nova subclasse após a compilação por meio de um dos jars? Portanto, o compilador pode dizer "Suas correspondências de padrões são exaustivas agora, mas isso pode mudar se alguma de suas dependências mudar", o que é bastante inútil, pois o objetivo de ter dependências externas é poder atualizá-las sem recompilar!
Doval

Daí o aviso. Na prática, se você fosse suficientemente acoplado para precisar de uma correspondência exaustiva de uma dependência, externa ou não, convém recompilar de qualquer maneira.
Karl Bielefeldt

@Doval Então você deve usar alguma forma de delegação e deixar a classe chamada decidir o que fazer e inverter o controle. É para isso que a OOP foi criada. Se você não quiser isso, terá seu problema atual.
Sled

@ArtB Não vejo o que delegação ou inversão de controle tem a ver com isso.
Doval

Se você deseja que ele trabalhe com pessoas que possam adicioná-lo externamente, chame a classe e mova a lógica para essas classes.
Sled
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.