¿Hay alguna forma de exigir explícitamente en Julia (por ejemplo, dentro de un módulo o paquete) que los tipos deben declararse ? ¿Por ejemplo, PackageCompiler
o Lint.jl
tiene algún soporte para tales controles? En términos más generales, ¿la distribución estándar de Julia proporciona algún analizador de código estático o equivalente que pueda ayudar a verificar este requisito?
Como un ejemplo motivador, digamos que queremos asegurarnos de que nuestra creciente base de códigos de producción solo acepte códigos que siempre sean de tipo declarado , bajo la hipótesis de que las bases de código grandes con declaraciones de tipo tienden a ser más fáciles de mantener.
Si queremos hacer cumplir esa condición, ¿proporciona Julia en su distribución estándar algún mecanismo para exigir una declaración de tipo o ayudar a avanzar en ese objetivo? (por ejemplo, ¿algo que pueda verificarse a través de linters, commit hooks o equivalente?)
fuente
hasmethod(f, (Any,) )
volveráfalse
si no se ha definido un genérico. Sin embargo, aún necesitaría hacer coincidir el número de argumentos (es decir,hasmethod(f, (Any,Any) )
para una función de dos argumentos).Respuestas:
La respuesta corta es: no, actualmente no hay herramientas para el tipo que verifica su código Julia. Sin embargo, es posible en principio, y en el pasado se ha trabajado en esta dirección, pero no hay una buena manera de hacerlo en este momento.
La respuesta más larga es que las "anotaciones de tipo" son una pista falsa aquí, lo que realmente quiere es la verificación de tipo, por lo que la parte más amplia de su pregunta es en realidad la pregunta correcta. Puedo hablar un poco sobre por qué las anotaciones de tipo son un arenque rojo, algunas otras cosas que no son la solución correcta y cómo se vería el tipo correcto de solución.
Requerir anotaciones de tipo probablemente no logre lo que desea: uno podría poner
::Any
cualquier campo, argumento o expresión y tendría una anotación de tipo, pero no una que le diga a usted o al compilador algo útil sobre el tipo real de esa cosa. Agrega mucho ruido visual sin agregar ninguna información.¿Qué hay de requerir anotaciones de tipo concreto? Eso descarta simplemente ponerse
::Any
todo (que es lo que Julia hace implícitamente de todos modos). Sin embargo, hay muchos usos perfectamente válidos de tipos abstractos que esto haría ilegal. Por ejemplo, la definición de laidentity
función es¿Qué tipo de anotación concreta pondrías
x
bajo este requisito? La definición se aplica a cualquierax
, independientemente del tipo, ese es el punto de la función. La única anotación de tipo que es correcta esx::Any
. Esto no es una anomalía: hay muchas definiciones de funciones que requieren tipos abstractos para ser correctas, por lo que obligar a aquellos a usar tipos concretos sería bastante limitante en términos de qué tipo de código de Julia se puede escribir.Hay una noción de "estabilidad tipográfica" de la que a menudo se habla en Julia. El término parece haberse originado en la comunidad de Julia, pero ha sido recogido por otras comunidades lingüísticas dinámicas, como R. Es un poco difícil de definir, pero significa aproximadamente que si conoce los tipos concretos de los argumentos de un método, También conoce el tipo de su valor de retorno. Incluso si un método es de tipo estable, eso no es suficiente para garantizar que escriba la verificación porque la estabilidad de tipo no habla de ninguna regla para decidir si algo tipo de verificación o no. Pero esto va en la dirección correcta: le gustaría poder verificar que cada definición de método sea de tipo estable.
Es posible que no desee exigir estabilidad de tipo, incluso si pudiera. Desde Julia 1.0, se ha vuelto común el uso de pequeños sindicatos. Esto comenzó con el rediseño del protocolo de iteración, que ahora se usa
nothing
para indicar que la iteración se realiza en lugar de devolver una(value, state)
tupla cuando hay más valores para iterar. Lasfind*
funciones en la biblioteca estándar también usan un valor de retorno denothing
para indicar que no se ha encontrado ningún valor. Estas son inestabilidades de tipo técnico, pero son intencionales y el compilador es bastante bueno para razonar acerca de cómo optimizar la inestabilidad. Entonces, al menos, los sindicatos pequeños probablemente deben estar permitidos en el código. Además, no hay un lugar claro para dibujar la línea. Aunque quizás se podría decir que un tipo de retorno deUnion{Nothing, T}
es aceptable, pero no hay nada más impredecible que eso.Sin embargo, lo que probablemente realmente desee, en lugar de requerir anotaciones de tipo o estabilidad de tipo, es tener una herramienta que verifique que su código no pueda arrojar errores de método, o tal vez en términos más generales que no arroje ningún tipo de error inesperado. El compilador a menudo puede determinar con precisión qué método se llamará en cada sitio de llamada, o al menos limitarlo a un par de métodos. Así es como genera código rápido: el despacho dinámico completo es muy lento (mucho más lento que vtables en C ++, por ejemplo). Si ha escrito un código incorrecto, por otro lado, el compilador puede emitir un error incondicional: el compilador sabe que cometió un error, pero no le dice hasta el tiempo de ejecución, ya que esas son las semánticas del lenguaje. Se podría requerir que el compilador pueda determinar qué métodos podrían llamarse en cada sitio de llamada: eso garantizaría que el código sea rápido y que no haya errores de método. Eso es lo que debería hacer una buena herramienta de verificación de tipos para Julia. Hay una gran base para este tipo de cosas ya que el compilador ya hace gran parte de este trabajo como parte del proceso de generación de código.
fuente
Esta es una pregunta interesante. La pregunta clave es qué definimos como tipo declarado . Si quiere decir que hay una
::SomeType
declaración en cada definición de método, entonces es algo difícil de hacer, ya que tiene diferentes posibilidades de generación de código dinámico en Julia. Quizás haya una solución completa en este sentido, pero no lo sé (me encantaría aprenderlo).Sin embargo, lo que me viene a la mente, que parece relativamente más simple, es verificar si algún método definido dentro de un módulo acepta
Any
como argumento. Esto es similar pero no equivalente a la declaración anterior como:se ve igual para la
methods
función ya que la firma de ambas funciones aceptax
comoAny
.Ahora para verificar si algún método en un módulo / paquete acepta
Any
como argumento a cualquiera de los métodos definidos en él, se podría usar algo como el siguiente código (no lo he probado exhaustivamente ya que lo acabo de escribir, pero parece que en su mayoría cubrir posibles casos):Ahora cuando lo ejecutas en el
Base.Iterators
módulo obtienes:y cuando, por ejemplo, comprueba el paquete DataStructures.jl obtiene:
Lo que propongo no es una solución completa a su pregunta, pero me pareció útil, así que pensé en compartirla.
EDITAR
El código anterior acepta
f
serFunction
solo. En general, puede tener tipos que se pueden llamar. Luego, lacheck_declared(m::Module, f::Function)
firma podría cambiarse acheck_declared(m::Module, f)
(en realidad, la función en sí misma permitiríaAny
como segundo argumento :)) y pasar todos los nombres evaluados a esta función. Luego, tendría que verificar simethods(f)
tiene un resultado positivolength
dentro de la función (en cuantomethods
a los valores no invocables devuelve un valor que tiene longitud0
).fuente