Mientras curioseaba en la página principal del sitio de un lenguaje de programación de scripts, encontré este pasaje:
Cuando un sistema se vuelve demasiado grande para tenerlo en la cabeza, puede agregar tipos estáticos.
Esto me hizo recordar que en muchas guerras de religión entre lenguajes compilados estáticos (como Java) y lenguajes dinámicos e interpretados (principalmente Python porque es más utilizado, pero es un "problema" compartido entre la mayoría de los lenguajes de secuencias de comandos), una de las quejas de estática Los fanáticos de los idiomas escritos en vez de los idiomas escritos dinámicamente es que no se adaptan bien a proyectos más grandes porque "un día, olvidarás el tipo de retorno de una función y tendrás que buscarlo, mientras que con los idiomas estáticamente escritos todo se declara explícitamente ".
Nunca entendí declaraciones como esta. Para ser honesto, incluso si declara el tipo de retorno de una función, puede y lo olvidará después de haber escrito muchas líneas de código, y aún tendrá que volver a la línea en la que se declara utilizando la función de búsqueda de su editor de texto para verificarlo.
Además, a medida que se declaran las funciones con type funcname()...
, sin saber type
que tendrá que buscar sobre cada línea en la que se llama la función, porque solo sabe funcname
, mientras que en Python y similares simplemente puede buscar def funcname
o function funcname
que solo sucede una vez, en la declaracion.
Más aún, con REPLs es trivial probar una función para su tipo de retorno con diferentes entradas, mientras que con lenguajes tipados estáticamente necesitaría agregar algunas líneas de código y recompilar todo solo para saber el tipo declarado.
Entonces, aparte de conocer el tipo de retorno de una función que claramente no es un punto fuerte de lenguajes tipados estáticamente, ¿cómo es realmente útil el tipeo estático en proyectos más grandes?
fuente
Respuestas:
No es trivial No es trivial en absoluto . Es trivial hacer esto para funciones triviales.
Por ejemplo, podría definir trivialmente una función en la que el tipo de retorno depende completamente del tipo de entrada.
En este caso,
getAnswer
realmente no tiene un solo tipo de retorno. No hay ninguna prueba que pueda escribir que llame a esto con una entrada de muestra para saber cuál es el tipo de retorno. Será siempre dependerá del argumento actual. En tiempo de ejecución.Y esto ni siquiera incluye funciones que, por ejemplo, realicen búsquedas de bases de datos. O hacer cosas basadas en la entrada del usuario. O busque variables globales, que por supuesto son de tipo dinámico. O cambie su tipo de retorno en casos aleatorios. Sin mencionar la necesidad de probar cada función individual manualmente cada vez.
Básicamente, probar el tipo de retorno de la función en el caso general es literalmente matemáticamente imposible (Problema de detención). La única forma de garantizar el tipo de retorno es restringir la entrada para que responder a esta pregunta no caiga dentro del dominio del Problema de detención al rechazar programas que no son demostrables, y esto es lo que hace la escritura estática.
Los lenguajes estáticamente escritos tienen cosas llamadas "herramientas". Son programas que te ayudan a hacer cosas con tu código fuente. En este caso, simplemente haría clic derecho e Ir a definición, gracias a Resharper. O use el atajo de teclado. O simplemente pasa el mouse y me dirá cuáles son los tipos involucrados. No me importa en lo más mínimo los archivos grepping. Un editor de texto por sí solo es una herramienta patética para editar el código fuente del programa.
De memoria,
def funcname
no sería suficiente en Python, ya que la función podría reasignarse arbitrariamente. O podría declararse repetidamente en múltiples módulos. O en clases. Etc.Buscar archivos para el nombre de la función es una operación primitiva terrible que nunca debería ser necesaria. Esto representa una falla fundamental de su entorno y herramientas. El hecho de que incluso consideres necesitar una búsqueda de texto en Python es un punto masivo contra Python.
fuente
Piense en un proyecto con muchos programadores, que ha cambiado con los años. Tienes que mantener esto. Hay una función
¿Qué demonios hace? ¿Qué es
v
? ¿De dóndeanswer
viene el elemento ?Ahora tenemos más información -; que necesita un tipo de
AnswerBot
.Si vamos a un lenguaje basado en clases podemos decir
Ahora podemos tener una variable de tipo
AnswerBot
y llamar al métodogetAnswer
y todos saben lo que hace. El compilador detecta los cambios antes de realizar cualquier prueba de tiempo de ejecución. Hay muchos otros ejemplos, pero ¿tal vez esto te da la idea?fuente
Parece tener algunas ideas erróneas sobre el trabajo con grandes proyectos estáticos que pueden estar nublando su juicio. Aquí hay algunos consejos:
La mayoría de las personas que trabajan con idiomas de tipo estático usan un IDE para el idioma o un editor inteligente (como vim o emacs) que tiene integración con herramientas específicas del idioma. Por lo general, hay una forma rápida de encontrar el tipo de función en estas herramientas. Por ejemplo, con Eclipse en un proyecto Java, hay dos formas en que normalmente encontrará el tipo de método:
someVariable.
) y Eclipse busca el tiposomeVariable
y proporciona una lista desplegable de todos los métodos definidos en ese tipo; A medida que me desplazo hacia abajo en la lista, se muestra el tipo y la documentación de cada uno mientras está seleccionado. Tenga en cuenta que esto es muy difícil de lograr con un lenguaje dinámico, porque es difícil (o en algunos casos imposible) para el editor determinar cuál es el tiposomeVariable
, por lo que no puede generar la lista correcta fácilmente. Si quiero usar un métodothis
, puedo presionar ctrl + espacio para obtener la misma lista (aunque en este caso no es tan difícil de lograr para los lenguajes dinámicos).Como puede ver, esto es algo mejor que las herramientas típicas disponibles para lenguajes dinámicos (no es que esto sea imposible en lenguajes dinámicos, ya que algunos tienen una funcionalidad IDE bastante buena; smalltalk es uno que se me ocurre), pero es más difícil para un lenguaje dinámico y, por lo tanto, es menos probable que esté disponible).
Las herramientas de lenguaje estático generalmente proporcionan capacidades de búsqueda semántica, es decir, pueden encontrar definiciones y referencias a símbolos particulares con precisión, sin necesidad de realizar una búsqueda de texto. Por ejemplo, usando Eclipse para un proyecto Java, puedo resaltar un símbolo en el editor de texto y hacer clic con el botón derecho y elegir 'ir a definición' o 'buscar referencias' para realizar cualquiera de estas operaciones. No necesita buscar el texto de una definición de función, porque su editor ya sabe exactamente dónde está.
Sin embargo, lo contrario es que la búsqueda de una definición de método por texto realmente no funciona tan bien en un proyecto dinámico grande como usted sugiere, ya que fácilmente podría haber múltiples métodos con el mismo nombre en dicho proyecto, y es probable que no tenga herramientas disponibles para desambiguarte a cuál de ellas estás invocando (porque tales herramientas son difíciles de escribir en el mejor de los casos o imposibles en el caso general), por lo que tendrás que hacerlo a mano.
No es imposible tener un REPL para un lenguaje de tipo estático. Haskell es el ejemplo que me viene a la mente, pero también hay REPL para otros lenguajes de tipo estático. Pero el punto es que no necesita ejecutar código para encontrar el tipo de retorno de una función en un lenguaje estático ; se puede determinar mediante un examen sin necesidad de ejecutar nada.
Es probable que incluso si necesitara hacer esto, no tuviera que volver a compilar todo . La mayoría de los lenguajes estáticos modernos tienen compiladores incrementales que solo compilarán la pequeña porción de su código que ha cambiado, para que pueda obtener comentarios casi instantáneos para errores de tipo si hace uno. Eclipse / Java, por ejemplo, resaltará los errores de escritura mientras los sigue escribiendo .
fuente
You seem to have a few misconceptions about working with large static projects that may be clouding your judgement.
Bueno, solo tengo 14 años y solo programo desde menos de un año en Android, así que es posible, supongo.Compare con say, javascript, Ruby o Smalltalk, donde los desarrolladores redefinen la funcionalidad del lenguaje central en tiempo de ejecución. Esto dificulta la comprensión del gran proyecto.
Los proyectos más grandes no solo tienen más personas, sino que también tienen más tiempo. Tiempo suficiente para que todos lo olviden o sigan adelante.
Como anécdota, un conocido mío tiene una programación segura de "Job For Life" en Lisp. Nadie, excepto el equipo, puede entender el código base.
fuente
Anecdotally, an acquaintance of mine has a secure "Job For Life" programming in Lisp. Nobody except the team can understand the code-base.
¿Es realmente tan malo? ¿La personalización que agregaron no les ayuda a ser más productivos?No se trata de que olvide el tipo de retorno; esto siempre va a suceder. Se trata de que la herramienta pueda hacerle saber que olvidó el tipo de devolución.
Esta es una cuestión de sintaxis, que no tiene relación alguna con la escritura estática.
La sintaxis de la familia C es de hecho hostil cuando desea buscar una declaración sin tener herramientas especializadas a su disposición. Otros idiomas no tienen este problema. Ver la sintaxis de la declaración de Rust:
Cualquier idioma puede ser interpretado y cualquier idioma puede tener un REPL.
Contestaré de manera abstracta.
Un programa consta de varias operaciones y esas operaciones se presentan como están debido a algunas suposiciones que hace el desarrollador.
Algunos supuestos son implícitos y otros explícitos. Algunos supuestos se refieren a una operación cerca de ellos, algunos se refieren a una operación lejos de ellos. Una suposición es más fácil de identificar cuando se expresa explícitamente y lo más cerca posible de los lugares donde su valor de verdad es importante.
Un error es la manifestación de una suposición que existe en el programa pero que no se cumple en algunos casos. Para rastrear un error, necesitamos identificar la suposición errónea. Para eliminar el error, necesitamos eliminar esa suposición del programa o cambiar algo para que la suposición realmente se mantenga.
Me gustaría clasificar los supuestos en dos tipos.
El primer tipo son los supuestos que pueden o no ser válidos, dependiendo de las entradas del programa. Para identificar una suposición errónea de este tipo, necesitamos buscar en el espacio todas las entradas posibles del programa. Usando conjeturas educadas y pensamiento racional, podemos reducir el problema y buscar en un espacio mucho más pequeño. Pero aún así, a medida que un programa crece incluso un poco, su espacio de entrada inicial crece a un ritmo enorme, hasta el punto en que puede considerarse infinito para todos los fines prácticos.
El segundo tipo son los supuestos que definitivamente son válidos para todas las entradas, o son definitivamente erróneos para todas las entradas. Cuando identificamos una suposición de este tipo como errónea, ni siquiera necesitamos ejecutar el programa o probar ninguna entrada. Cuando identificamos una suposición de este tipo como correcta, tenemos que preocuparnos de un sospechoso menos cuando estamos rastreando un error ( cualquier error). Por lo tanto, es valioso tener tantos supuestos como sea posible pertenecer a este tipo.
Para poner un supuesto en la segunda categoría (siempre verdadero o siempre falso, independiente de las entradas), necesitamos una cantidad mínima de información para estar disponible en el lugar donde se realiza el supuesto. A través del código fuente de un programa, la información se vuelve obsoleta bastante rápido (por ejemplo, muchos compiladores no hacen análisis interprocediales, lo que hace que cualquier llamada sea un límite difícil para la mayoría de la información). Necesitamos una manera de mantener actualizada la información requerida (válida y cercana).
Una forma es tener la fuente de esta información lo más cerca posible del lugar donde se va a consumir, pero eso puede ser poco práctico para la mayoría de los casos de uso. Otra forma es repetir la información con frecuencia, renovando su relevancia en todo el código fuente.
Como ya puede adivinar, los tipos estáticos son exactamente eso: balizas de información de tipo dispersas en el código fuente. Esa información se puede utilizar para colocar la mayoría de los supuestos sobre la corrección de tipo en la segunda categoría, lo que significa que casi cualquier operación se puede clasificar como siempre correcta o siempre incorrecta con respecto a la compatibilidad de tipos.
Cuando nuestros tipos son incorrectos, el análisis nos ahorra tiempo al llamar la atención sobre el error más temprano que tarde. Cuando nuestros tipos son correctos, el análisis nos ahorra tiempo al garantizar que cuando se produce un error, podemos descartar inmediatamente los errores de tipo.
fuente
Usted recuerda el viejo adagio "basura adentro, basura afuera", bueno, esto es lo que ayuda a prevenir la escritura estática. No es una panacea universal, pero la rigurosidad sobre qué tipo de datos acepta y devuelve una rutina significa que tiene la seguridad de que está trabajando correctamente con ella.
Por lo tanto, una rutina getAnswer que devuelve un número entero no será útil cuando intente usarlo en una llamada basada en cadenas. La escritura estática ya te dice que tengas cuidado, que probablemente estés cometiendo un error. (y claro, puedes anularlo, pero tendrías que saber exactamente qué es lo que estás haciendo, y especificarlo en el código usando un yeso. Sin embargo, en general, no quieres estar haciendo esto - pirateando un la clavija redonda en un agujero cuadrado nunca funciona bien al final)
Ahora puede llevarlo más allá al usar tipos complejos, al crear una clase que tenga funcionalidad adicional, puede comenzar a pasarlos y de repente obtendrá mucha más estructura en su programa. Los programas estructurados son aquellos que son mucho más fáciles de hacer que funcionen correctamente y que también se mantengan.
fuente