¿Por qué el compilador Scala no puede dar advertencia de coincidencia de patrones para clases / rasgos no sellados?

10

Si uso un sin sellar traito abstract classen Scala y luego uso la coincidencia de patrones, me pregunto, ¿el compilador no sabe en el momento de la compilación para este patrón en particular qué posibles implementaciones de este rasgo / clase están disponibles? Entonces, si lo hace, ¿no podría dar advertencias de coincidencia de patrones a pesar de que trait/ abstract classno está sellado porque sabe qué tipos podrían usarse, al verificar todas las dependencias / importaciones posibles?

Por ejemplo, si tengo una Option[A]coincidencia de patrones y solo para Some[A]pero no para None, el compilador se quejará porque Optionestá sellado.

Si el compilador no puede saber / resolver eso, ¿por qué no puede hacerlo? Y si el compilador (en teoría) puede hacer eso, ¿cuáles son las razones por las que no se usa en Scala? ¿Hay otros idiomas que soporten ese tipo de comportamiento?

valenterry
fuente
No está claro lo que estás preguntando. ¿Desea que el compilador emita una advertencia si la expresión de coincidencia no cubre todas las entradas posibles? Quizás un ejemplo aclararía su pregunta.
kdgregory
2
Si alguien puede introducir una nueva subclase, la coincidencia de patrones nunca puede ser exhaustiva. Por ejemplo, produce una clase abstracta Foocon subclases A, By C, y todas sus coincidencias de patrones coinciden solo con esas tres. Nada me impide agregar una nueva subclase Dque explote tus coincidencias de patrones.
Doval
@kdgregory Sí, lo tienes. Agregué un ejemplo para hacerlo más claro.
valenterry
3
Verificar todas las importaciones no es suficiente para descubrir todas las subclases posibles. Otra subclase podría declararse en un archivo de clase separado que luego se carga durante el tiempo de ejecución a través de java.lang.ClassLoader.
amon

Respuestas:

17

Resolver todas las subclases de una clase se llama Análisis de jerarquía de clases, y hacer CHA estático en un lenguaje con carga de código dinámico es equivalente a resolver el Problema de detención.

Además, uno de los objetivos de Scala es la compilación y el despliegue separados de módulos independientes, por lo que el compilador simplemente no puede saber si una clase está subclaseada en otro módulo, ya que nunca analiza más de un módulo. (¡Después de todo, puede compilar un módulo contra la interfaz de algún otro módulo sin que ese módulo exista en su sistema!) Es por eso sealedque todas las subclases se definen en la misma unidad de compilación.

Esa es también una de las razones por las cuales las JVM pueden competir tan favorablemente con los compiladores de C ++: los compiladores de C ++ suelen ser compiladores estáticos, por lo que, en general, no pueden determinar si un método está anulado o no, y por lo tanto no pueden alinearlo. JVMs OTOH, generalmente son compiladores dinámicos, no necesitan realizar CHA para determinar si un método se anula o no, solo pueden mirar la Jerarquía de clases en tiempo de ejecución. E incluso si en un momento posterior en la ejecución del programa aparece una nueva subclase que no estaba allí antes, no es gran cosa, simplemente recompile ese fragmento de código sin incluirlo.

Nota: todo esto solo se aplica dentro de Scala. La JVM no tiene noción de sealed, por lo que es perfectamente posible subclasificar las sealedclases de otro lenguaje JVM, ya que no hay forma de comunicar esto a otro idioma. La sealedpropiedad se registra en la ScalaSiganotación, pero los compiladores de otros idiomas no tienen en cuenta esas anotaciones, obviamente.

Jörg W Mittag
fuente
3

Se puede hacer (al menos para todas las clases conocidas en tiempo de compilación), es simplemente costoso. Destruiría por completo la compilación incremental, porque todo lo que contenga una coincidencia de patrones debería volver a compilarse cada vez que se modifique cualquier otro archivo.

Y que estas comprando? Es un olor a código para escribir coincidencias de patrones que deben cambiar con frecuencia cuando se agrega una nueva clase derivada. Es una violación del principio abierto / cerrado . Use la herencia correctamente y no necesitará escribir ese tipo de coincidencias de patrones. Y sí, el principio abierto / cerrado también se aplica a lenguajes funcionales sin herencia basada en clases. De hecho, entre características como clases de tipos, métodos múltiples y funciones simples de orden superior, los lenguajes funcionales hacen que la extensión sin modificación sea mucho más fácil.

Karl Bielefeldt
fuente
1
It can be done (at least for all classes known at compile time), it's just expensive.Pero si el programa no es 100% autónomo (es decir, depende de .jararchivos externos ), ¿no podría colarse en una nueva subclase después de compilar a través de uno de los jars? Por lo tanto, el compilador podría decirle "Sus coincidencias de patrones son exhaustivas ahora, pero eso puede cambiar si alguna de sus dependencias cambia", lo que no tiene ningún valor ya que el punto de tener dependencias externas es poder actualizarlas sin volver a compilar.
Doval
De ahí el descargo de responsabilidad. En la práctica, si estuvieras lo suficientemente acoplado como para necesitar una coincidencia exhaustiva en una dependencia, externa o de otro tipo, querrás volver a compilar de todos modos.
Karl Bielefeldt
@Doval Luego, debe usar alguna forma de delegación y dejar que la clase que se llama decida qué hacer e invierta el control. Para esto estaba destinado OOP. Si no quieres eso, entonces tienes tu problema actual.
Trineo
@ArtB No veo qué delegación o inversión de control tiene que ver con esto.
Doval
Si desea que funcione con personas que puedan agregarle externamente, llame a la clase y mueva la lógica a esas clases.
Trineo