Se eu usar um un selado trait
ou abstract class
no 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 class
nã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 Option
está 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?
fonte
Foo
com subclassesA
,B
eC
, e todos os seus jogos padrão de correspondência apenas aqueles três. Nada me impede de adicionar uma nova subclasseD
que exploda suas correspondências de padrões.java.lang.ClassLoader
.Respostas:
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
sealed
exige 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 subclassificarsealed
classes de outro idioma da JVM, pois não há como comunicar isso para outro idioma. Asealed
propriedade é registrada naScalaSig
anotação, mas os compiladores de outros idiomas não levam em conta essas anotações, obviamente.fonte
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.
fonte
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.jar
arquivos externos ), você não poderá entrar em uma nova subclasse após a compilação por meio de um dosjar
s? 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!