Ayer pasé parte del día entretenido con GfxBuilder he intentando hacer un gráfico estadístico para el tablero Kanban en el que ando metido.
Registro las fechas en que cada pegatina sale de la primera columna (se supone que de alguna manera se empieza a trabajar en ella) y la fecha en que llega a la última (se supone que se termina el trabajo en ella), con la intención de hacer algún gráfico que nos de idea de cuánto tarda en promedio una pegatina en recorrer todas las columnas, cuantas hay a medio hacer cada día, etc.
Así que en el momento de visualizar el gráfico, recolecto toda esa información y me pongo a echar cuentas para definir los puntos a dibujar. Una vez hecho, intento dibujar una polyline de GfxBuilder…. y me da un ClassCastException. Dice que no puede hacer cast de ArrayList a Number. Bueno, me habré equivocado, seguro que dentro de donde se espera un número he metido sin querer algún tipo de lista. Miro, remiro y reviso y nada, todo aparentemente está correcto. Así que quito mi código e intento dibujar, con copy-paste, la polyline del ejemplo. Vaya, da exactamente la misma excepción y el ArrayList que no puede convertir es el de los puntos.
Me pongo con google y acabo en el repositorio de fuentes de GfxBuilder y en concreto, en la clase PolyLineNode.groovy, que es la que se supone pinta las polilíneas. Curioso, el atributo points que es donde se supone debe estar el array de puntos se declara como double. Y ello no les impide, en otros sitios del código, hacer cosas como points.size(). No creo equivocarme, pero tiene pinta clara de ser un bug/despiste.
Y esto, aunque pueda ser un despiste, es realmente un problema. He hecho una prueba mínima y groovy compila eso sin cantar error. A pesar de estar puesto el tipo de points como double, permite llamar a size(). Desde mi punto de vista, este es el motivo por el que lenguajes que no son fuertemente tipados quedan descartados para grandes desarrollos. Todos cometemos despistes en algún momento y si somos un equipo de muchos desarrolladores en un proyecto, la probabilidad de despistes es muy alta. Y en este caso, el despiste que un lenguaje fuertemente tipado detecta en tiempo de compilado (e incluso un buen IDE en el momento mismo de escribirlo), se ha colado hasta la cocina, pasando por el repositorio y al público en general. Sí, diréis que hay que ser disciplinados, pero errar es humano y sobre todo en proyectos grandes con muchos humanos codificando. Eso sí, eso no quita que la gente de GfxBuilder haya dado por bueno un código del que ni siquiera han hecho un ejemplo básico para verlo funcionando.
Y seguimos pintando gráficos. Al no poder usar polyline, me dije, voy a pintarlo a base de muchos line. Hago el código, pruebo… y el gráfico sale vacío, sin errores ninguno, pero vacío. Reviso el contenido de los datos y es correcto, están dentro de los rangos de pixels de la pantalla y demás. Empiezo a probar cosas y en un momento dado se me ocurre dibujar junto a mis líneas una elipse usando la primitiva ellipse y curioso, me sale le elipse… y todas las líneas que antes no salían. Borro la línea de ellipse y entonces no sale nada. Pongo un circle y ya sale el círculo y las líneas. ¿Es posible que las líneas no salgan si no se dibuja además otro tipo de cosa que no sea una línea?
Bueno, pues el gráfico ya va avanzando, me ha costado más de lo debido por este tipo de cosas y seremos comprensivos con el asunto. GfxBuilder es joven y todavía está en version 0.2.3, por lo que entiendo está todavía en desarrollo y, por lo que veo, con "sus cosas".
Bueno estan claras las ventajas de un lenguaje fuertemente tipado: como decia en otro comentario es una medida defensiva contra los previsibles errores de los programadores tan humanos ellos. Eso hace el tipado explicito estatico tan util en entornos de equipos de programacion con poca experiencia. Pero tambien tiene un doble filo: el acostumbrarte a que el ide piense por ti lo que estas escribiendo hace que escribas mas rapido, pero pensando menos tu mismo en lo que estas escribiendo y te da una a veces false sensacion de seguridad que a veces te hace mas facil el pasar de las pruebas. El dia que escribes algo mal que el ide no te avisa runtime exception de los gordisimos. En java tambien te puedes saltar el tipado estatico (afortunadamente): usar mapas o listas como parametros y fundamentalmente con la reflexion. El error en este caso del que ha hecho la libreria ha sido el no hacer las pruebas (previas en plan tdd o posteriores de toda la vida) y las pruebas las tienes que hacer con java, con groovy o con haskell (que tiene el sistema de tipado estatico mas sublime que conozco). Yo tambien he sentido la agradable sensacion de seguridad de refactorizar sin ningun error de compilacion, sensacion rota en cuanto el fiarme de no tener ninguna marca roja tuve un bug en produccion que el haber compilado todo ok no me salvo.
Y a cambio que tienes: el poder añadir metodos o campos en tiempo de ejecucion a las clases (y a sus instancias), el convertir un map en una instancia de una clase en una linea.. el ide no canta que Double.size() no existe porque en tiempo de ejecucion puede existir perfectamente aunque no este definido el codigo fuente de la clase.
Con esto no quiero dejar de lado absolutamente el tipado estatico en general pero para tener el anemico tipado estatico de java que te hace escribir (o generar codigo) redundante mejor no tenerlo. Pero bueno, puedes tipar las variables en groovy creo y tambien existe una version de groovy (groovy++) que recupera el tpado estatico de java aunque no lo he probado directamente.
¿Seguro que se puede cambiar el contenido del double para que sea otra cosa?. Al intentarlo parece que salta el ClassCastException. Entiendo que si no pones tipo (estilo def points), entonces si puedes meter lo que te de la gana sobre la marcha, pero si pones tipo (double points), ya está condenado a ser un double.
En cuanto a tipado y no tipado, vamos a poner las cosas en igualdad de condiciones. Independientemente de tipado o no tipado, hacer TDD, test unitarios y todas las pruebas del mundo durante el desarrollo, no te libran de que algún bug se pueda colar en producción. Simplemente, en igualdad de condiciones (misma experiencia de desarrolladores, misma metodología de trabajo, etc), en un no tipado hay más probabilidades de que se te cuelen errores hasta producción, y me remito al del ejemplo, que con un lenguaje tipado no habría llegado hasta ahí, incluso siguiendo el mismo tipo de desarrollo.
Al final supongo que es como todo, cada lenguaje tiene su porqué y es bueno para cosas en las que cojea otro. Está claro que para aplicaciones web se han impuesto lenguajes no tipados e interpretados, como python, por lo que imagino que tienen ventajas fuertes en este tipo de aplicaciones respecto a java u otros lenguajes.
Bueno poder se puede, no se si este es el caso, igual es un bug pero me resulta extraño la verdad.
Ejemplo de una sesion grails shell:
c:
Welcome to Grails 1.2.0 – http://grails.org/
Licensed under Apache Standard License 2.0
Grails home is set to: C:\myGrailsApp
Base Directory: C:\myGrailsApp
Resolving dependencies…
Dependencies resolved in 671ms.
Running script C:\myGrailsInstall\grails\grails-1.2.0\scripts\Shell.gro
ovy
Environment set to development
Groovy Shell (1.6.7-SNAPSHOT, JVM: 1.6.0_14)
Type ‘help’ or ‘\h’ for help.
——————————————————————————-
groovy:000> d=(Double) 1.0
===> 1.0
groovy:000> d instanceof Double
===> true
groovy:000> d.size()
ERROR groovy.lang.MissingMethodException: No signature of method: java.lang.Doub
le.size() is applicable for argument types: () values: []
at groovysh_evaluate.run (groovysh_evaluate:2)
…
groovy:000> Double.metaClass.size= {-> delegate}
===> groovysh_evaluate$_run_closure1@7fb878
groovy:000> d2=(Double) 2.0
===> 2.0
groovy:000> d.size()
===> 1.0
groovy:000> d2.size()
===> 2.0
groovy:000> d2 instanceof Double
===> true
groovy:000> ((Double)4.6).size()
===> 4.6
groovy:000>
Esto es un ejemplo sin mucho sentido de lo que se puede hacer con groovy por ser tipado dinamicamente pero no se si lo de la clase esa es un bug. Siempre puedes cambiarla dinamicamente y que ese atributo sea una lista en lugar de un double sin cambiar el codigo de la clase 😉
groovy:000> class Ejemplo {BigDecimal d=1.0}
===> true
groovy:000> ej=[d:2.0] as Ejemplo
===> Ejemplo@66db21
groovy:000> ej.d
===> 2.0
groovy:000> ej.d.size()
ERROR groovy.lang.MissingMethodException: No signature of method: java.math.BigD
ecimal.size() is applicable for argument types: () values: []
at groovysh_evaluate.run (groovysh_evaluate:2)
…
groovy:000> ej.d=[]
ERROR org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast
object ‘[]’ with class ‘java.util.ArrayList’ to class ‘java.math.BigDecimal’
at Ejemplo.setProperty (groovysh_evaluate)
at groovysh_evaluate.run (groovysh_evaluate:2)
…
groovy:000> Ejemplo.metaClass.d=[]
===> []
groovy:000> ej.d.size()
===> 0
groovy:000> ej.d.add 2.5
===> true
groovy:000> ej.d
===> [2.5]
ok. Es curioso el asunto, intentaré jugar un poco con ello a ver.
En cualquier caso, en el gfxbuilder el problema es que ni siquiera puede meter el ArrayList que se pasa como parámetro en el «double points», por lo que la llamada a size() no llega a hacerse en la vida 😉
Estoy más de acuerdo con chuidiang al respecto de los lenguajes tipados, pero no únicamente las razones de «experiencia» lo justifican. El desarrollo bajo «presión» también provoca errores, o un mal diseño en la comunicación entre dos sistemas puede hacer que no se sepa que datos se envían entre ellos.
En cuanto al IDE, es una ayuda y te simplifica la vida, pero eso no implica que se deban relajar las pruebas. Y un deber de un lider de proyecto es realizar el seguimiento de la planificación para que no se desvíe de lo trazado y si se desvía para corregirla.
Me temo que en el tema de integracion entre sistemas a poco «decoupling» que haya entre ellos (pueden ser difeentes tecnologias, diferentes lenguajes y como poco tener una interfaz de integracion estandar) que cada uno de los lenguajes de los sitemas implicados este tipado estaticamente no se si te ayuda a encontrar bugs de integracion.
Sin embargo creo que mi argumento mas importante es como afecta a la actitud del programador tanto el usar un ide como el copypaste, la generacion automatica de codigo o a oro nivel el tipado estatico. Todas esas practicas te hacen mas productivo a costa de perder agudeza y no tener recursos. En un programador recien salido del horno es catastrofico pues el haber usado siempre muletas los convierte en inutiles en cuanto por cualquier razon no puedes contar con esas herramientas. ¿Cuantos «programadores» java no saben hacer una clase en un notepad compilarla con javac y ejecutarla con java en linea de comandos? ¿Cuantos no saben ni que lo es ant (o make o lo que sea) ni mucho menos hacerse uno para gestionar su proyecto? ¿Cuantos se dedican mas editar xml que a programar? Me temo que cada vez mas… Claro que si tienes al mago detras para arreglarte las cosas cuando no tienes muletas pues la cosa va tirando. Mientras haya magos claro
Por cierto he dado parte a ver si lo arreglan:
http://jira.codehaus.org/browse/GRIFFON-153
Bueno parece que se ha arreglado la cosa en la nueva version del GfxBuilder y del plugin:
http://jira.codehaus.org/browse/GRIFFON-153
Si, la verdad es que ha ido rápido el arreglo.
Hola, estoy de acuerdo en que los lenguajes dinámicos tienen un posibilidad más de error que los no dinámicos, pero decir que no son fiables porque en un desarrollo no se ha ejecutado una prueba concreta es un poco «tendencioso». A ver cuantos NullPointer han saltado en java en algún momento por no hacer la prueba correcta, compilador incluido.
Dicho esto, igual es cuestión de hábitos. En los lenguajes no dinámicos hay que compilar, y en los dinámicos hay que hacer pruebas unitarias. No recuerdo quién decía que dentro de unos años la compilación será vista como una forma de prueba unitaria simple, vete a saber si tenía razón…
Otra cosa que me llama la atención es lo de que «lenguajes que no son fuertemente tipados quedan descartados para grandes desarrollos». Habría que definir grandes desarrollos, pero creo que hay cosas bastante curiosas con lenguajes dinámicos. (Por cierto IMHO «lenguajes debilmente tipados»!=dinámicos).
Lo que tengo claro es que para gustos los colores 😉 Es lo bueno del software, hay lenguajes para todos (alguno se ha hecho el suyo propio).
Pingback: de la red – 22/03/2010 « Tecnologías y su contexto
Hola, soy el creador de GfxBuilder. Me gustaria saber mas sobre el codigo que utilizaste en tus pruebas (a sabiendas que GfxBuilder 0.2.4 corrige el problema en Polyline) para asi mejorar el proyecto. Es cierto que GfxBuilder no tiene un numero alto de pruebas unitarias, sin embargo el codigo esta basado en GraphicsBuilder, el cual si tenia mayor numero de pruebas y uso externo.
Saludos,
Andres
@Andres Hola, el código está en github, en concreto en la clase EstadisticaController.groovy.
Corregido el error de polyline, sólo me queda que si sólo pintas line, no se pintan. Por ejemplo, esto no pinta nada
pero esto otro pinta la línea y el círculo
Hmm comprobado que existe un problema con line() cuando es el unico elemento dentro de group() o con elementos que no proveen una figura geometrica. Creo que sera necesario realizar una busqueda exhaustiva.
@Andres, ok, gracias por la rápida respuesta, tanto ahora como en el caso de la polyline 🙂
Que haya gente como aalmiray trabajando en proyectos de groovy hace que mire de otra forma la comunidad, ¡eso es un lider de proyecto opensource!
No se si esto es siempre asi, pero de soporte no se puede uno quejar, en este caso al menos.
Ya quedo registrado este problema en JIRA, les presento la liga por si estan interesados en seguir el avance y/o comentar problemas relacionados.
http://jira.codehaus.org/browse/GRIFFON-154
Saludos,
Andres
Pingback: Diario de Programación » Blog Archive » Incidencia resuelta en GfxBuilder