2009-05-19 13 views
6

Me parece que lo más valioso de un lenguaje de programación estático/fuertemente tipado es que ayuda a la refactorización: si/cuando cambias alguna API, entonces el compilador te dirá qué cambio ha roto.Mecanografía estática/fuerte y refactorización

Puedo imaginarme escribiendo código en tiempo de ejecución/lenguaje débilmente tipado ... pero no me puedo imaginar refactorizar sin la ayuda del compilador, y no me puedo imaginar escribiendo decenas de miles de líneas de código sin refactorizar.

¿Es esto cierto?

+0

Aún podría refactorizar, solo usaría pruebas unitarias como reemplazo del IDE/compilador. Realmente no es * tan * malo una vez que te acostumbras. –

Respuesta

13

Creo que se está combinando cuando se comprueban los tipos con la forma en que se comprueban. La tipificación en tiempo de ejecución no es necesariamente débil.

La principal ventaja de los tipos estáticos es exactamente lo que dices: son exhaustivos. Puede estar seguro de que todos los sitios de llamadas se ajustan al tipo simplemente dejando que el compilador lo haga.

La principal limitación de los tipos estáticos es que están limitados en las restricciones que pueden expresar. Esto varía según el idioma, con la mayoría de los idiomas que tienen sistemas de tipo relativamente simple (c, java) y otros con sistemas de tipo extremadamente poderoso (haskell, cayena).

Debido a esta limitación, los tipos por sí solos no son suficientes. Por ejemplo, en Java los tipos están más o menos restringidos a la verificación de nombres de tipos coincidentes. Esto significa que el significado de cualquier restricción que desee comprobar debe codificarse en un esquema de nombres de algún tipo, de ahí la gran cantidad de indirecciones y placas de calderas comunes al código de Java. C++ es un poco mejor ya que las plantillas permiten un poco más de expresividad, pero no se acerque a lo que puede hacer con los tipos dependientes. No estoy seguro de cuáles son las desventajas de los sistemas de tipo más potentes, aunque es evidente que debe haber alguna o más personas que los utilicen en la industria.

Incluso si está usando el tipado estático, es probable que no sea lo suficientemente expresivo para verificar todo lo que le importa, por lo que también tendrá que escribir pruebas.Si el tipado estático te ahorra más esfuerzo de lo que requiere en repetitivo es un debate que se ha desatado durante siglos y que no creo que tenga una respuesta simple para todas las situaciones.

En cuanto a su segunda pregunta:

¿Cómo podemos re-factor de forma segura en un lenguaje escrito en tiempo de ejecución?

La respuesta son las pruebas. Sus pruebas tienen que cubrir todos los casos que importan. Las herramientas pueden ayudarlo a medir cuán exhaustivas son sus pruebas. Las herramientas de comprobación de cobertura le permiten saber si las líneas de código están cubiertas por las pruebas o no. Las herramientas de prueba de la mutación (bufón, heckle) pueden indicarle si sus pruebas son lógicamente incompletas. Las pruebas de aceptación le permiten saber qué ha escrito y cumple los requisitos, y, por último, las pruebas de regresión y rendimiento garantizan que cada versión nueva del producto mantenga la calidad de la última.

Una de las mejores cosas de contar con pruebas adecuadas en lugar de depender de complejas indirecciones de tipo es que la depuración es mucho más simple. Al ejecutar las pruebas, se obtienen aserciones fallidas específicas dentro de las pruebas que expresan claramente lo que están haciendo, en lugar de declaraciones obtusas de error del compilador (piense en errores de plantilla de C++).

No importa qué herramientas use: escribir código en el que confíe requerirá esfuerzo. Lo más probable es que requiera escribir muchas pruebas. Si la penalización por errores es muy, como el software de control aéreo o espacial, es posible que necesite utilizar métodos matemáticos formales para probar el comportamiento de su software, lo que hace que dicho desarrollo sea extremadamente costoso.

+0

¿Podría explicar qué es lo que falta en los sistemas de tipo estático que realmente quiero? Por lo que puedo decir, no siento ninguna falta que pueda atribuirse al sistema de tipos, ni lo he hecho desde que aprendí C++ hace algunos años. Podría estar extrañando algo y no darme cuenta, en cuyo caso sería muy valioso que me lo señalaras. –

+0

@John: Alguien en Lambda the Ultimate podría responderte mejor, pero desde mi limitado entendimiento, parece que el punto débil de la escritura en C++ es la restricción de los agregados. El pirateo de plantilla C++ a la alexandrescue puede llevarlo bastante lejos, pero creo que todavía hay bastante código relacionado con contenedor/matriz que recurre a verificaciones en tiempo de ejecución. Por ejemplo, ¿qué pasaría si quisiera restringir la forma de un vector? ¿O limitar la longitud de una cuerda a un rango? Los tipos dependientes pueden expresar este tipo de cosas. –

+0

No he usado un idioma con 'tipos dependientes' ... mi noción de 'tipeo en tiempo de ejecución' se parece más a JavaScript. – ChrisW

1

Diría que la refactorización va más allá de lo que el compilador puede comprobar, incluso en lenguajes estáticos. La refactorización solo está cambiando la estructura interna de un programa sin afectar el comportamiento externo. Incluso en lenguajes dinámicos, todavía hay cosas que puede esperar que ocurran y prueben, solo pierde un poco de ayuda del compilador.

5

Estoy totalmente de acuerdo con su opinión. La misma flexibilidad en la que se supone que los lenguajes tipados dinámicamente son buenos es en realidad lo que hace que el código sea muy difícil de mantener. Realmente, ¿hay algo así como un programa que continúa funcionando si los tipos de datos se cambian de una manera no trivial sin cambiar realmente el código?

Mientras tanto, puede verificar el tipo de variable que se pasa, y de alguna manera fallar si no es del tipo esperado. Todavía tendría que ejecutar su código para eliminar esos casos, pero al menos algo podría decirle.

Creo que las herramientas internas de Google realmente hacen una compilación y probablemente escriban cheques a su Javascript. Ojalá tuviera esas herramientas.

+1

¿No está básicamente hablando de Google Web Toolkit (http://code.google.com/webtoolkit/)? –

+0

"Realmente, ¿hay algo así como un programa que continúa funcionando si los tipos de datos se cambian de una manera no trivial sin cambiar realmente el código?" Sí, en cualquier idioma que sea compatible con el tipado estructural (también conocido como pato escribiendo) con frecuencia verá esto. Esto no es necesariamente estático o dinámico. Por ejemplo, las funciones genéricas de C++ solo se preocupan por los aspectos específicos de los tipos que utilizan, por lo que son posibles muchos cambios en los tipos de argumentos que no requieren cambiar la función. –

1

Una de las ventajas de usar var en C# 3.0 es que se puede menudo cambiar el tipo sin romper ningún código. El tipo debe tener el mismo aspecto: las propiedades con los mismos nombres deben existir, los métodos con la misma o similar firma aún deben existir. Pero realmente puede cambiar a un tipo muy diferente, incluso sin usar algo como ReSharper.

2

Para empezar, soy un programador nativo de Perl, así que por un lado nunca he programado con la red de tipos estáticos. OTOH Nunca he programado con ellos, así que no puedo hablar de sus beneficios. Con lo que puedo hablar es sobre cómo refactorizar.

No me parece que la falta de tipos estáticos sea un problema para la refactorización. Lo que encuentro un problema es la falta de un refactorizador navegador. Los lenguajes dinámicos tienen el problema de que no se sabe realmente qué hará realmente el código hasta que realmente lo ejecute. Perl tiene esto más que la mayoría. Perl tiene el problema adicional de tener una sintaxis muy complicada, casi imposible de analizar. Resultado: sin herramientas de refactorización (aunque they're working very rapidly on that). El resultado final es que tengo que refactorizar a mano. Y eso es lo que introduce errores.

Tengo pruebas para detectarlas ... por lo general. A menudo me encuentro frente a una pila humeante de código no comprobado y casi imposible de probar con el problema del huevo/gallina de tener que refactorizar el código para probarlo, pero tener que probarlo para poder refactorizarlo. Ick. En este punto, tengo que escribir algunas pruebas muy tontas, de alto nivel, "¿el programa emite lo mismo que antes?" Solo para asegurarme de no romper algo.

Los tipos estáticos, tal como se prevé en Java o C++ o C#, realmente solo resuelven una pequeña clase de problemas de programación. Garantizan que sus interfaces pasan bits de datos con la etiqueta correcta. Pero el hecho de que obtenga una Colección no significa que la Colección contenga los datos que usted cree que tiene. Porque obtienes un número entero no significa que tienes el número entero correcto. Su método toma un objeto Usuario, pero ¿ese usuario inició sesión?

Ejemplo clásico: public static double sqrt(double a) es el signature for the Java square root function. La raíz cuadrada no funciona en números negativos. ¿Dónde dice eso en la firma? No es así Peor aún, ¿dónde dice qué función tiene esa función? La firma solo dice qué tipos toma y qué devuelve. No dice nada sobre lo que sucede en el medio y ahí es donde vive el código interesante.Algunas personas han intentado capturar la API completa usando design by contract, que en líneas generales se puede describir como la incrustación de pruebas de ejecución de las entradas, salidas y efectos secundarios de su función (o la falta de ella) ... pero ese es otro espectáculo.

Una API es mucho más que simples firmas de funciones (si no fuera así, no necesitarías toda esa prosa descriptiva en los Javadocs) y la refactorización es mucho más que simplemente cambiar la API.

La mayor ventaja de refactorización es que un lenguaje no dinámicamente compilado y estáticamente estático te da la capacidad de escribir herramientas de refactorización para hacer refactorizaciones bastante complejas porque sabe dónde están todas las llamadas a tus métodos. Estoy muy envidioso de IntelliJ IDEA.

+1

"Una API es mucho más que solo firmas de funciones" ... Si cambio el significado de un método sin cambiar su firma, entonces puedo simplemente cambiar el nombre del método: entonces el compilador me ayudará/requerirá que encuentre todos los lugares en el resto del programa que usan el nombre antiguo y esperan el viejo significado. – ChrisW