Archivo de la etiqueta: Desarrollo

Aprendiendo haskell

Ya hace unos meses que, con tiempo que dispongo, estoy aprendiendo haskell. El motivo principal es que quería empezar con un lenguaje que fuera completamente distinto a los que ya conozco.

Confieso que de momento me parece todo un galimatías. Si bien llego a resolver algunos problemas sencillos, aún me pierdo con cosas medianamente complejas.

Además, para ir aprendiendo e ir estrujándome los sesos utilizo codewars para ir resolviendo ejercicios. Creo que es un buen modo para ir tomando contacto con el lenguaje.

¿Qué me aporta haskell?

La verdad es que no lo estoy estudiando porque sí. El objetivo es poder aprender nuevos mecanismos para afrontar los problemas. Haskell te obliga a afrontarlos desde una perspectiva completamente diferente a lo que es habitual en los lenguajes imperativos. Con ello espero que a futuro pueda incorporar nuevas formas para resolver problemas a mi día a día (siempre que valgan la pena).

¿Por qué haskell?

Porque al ser un lenguaje funcional puro me obliga a desconectar totalmente de lo que ya sé. Hay otros lenguajes funcionales, pero no llegan a la pureza de haskell¹ lo que me podría permitir desviarme de la intención de aprender programación funcional.

¿Qué haré con él?

Cuando tenga más claro como funciona y no me pierda tanto me pondré con yesod, un framework para desarrollo web. Creo que será toda una experiencia.

¿Y después?

Una vez que ya tenga haskell dominado espero poder ponerme con Rust. La verdad es que de un tiempo a esta parte ando muy desencantado con C. Cada día me parece peor lenguaje de programación, pero eso ya será otro artículo.

Notas

  1. Probablemente haya más lenguajes funcionales puros, pero haskell es de los que tiene la comunidad más amplia y, por tanto, en la que es más fácil encontrar documentación.

La necesidad de invertir en herramientas

Supongo que de una vez os habrá  pasado que ante ciertas tareas (tanto de desarrollo como de administración) repetís una y otra vez las mismas acciones o el mismo código. Otras veces, simplemente os encontráis que hay cosas que son fáciles de automatizar, pero no se hace (por ejemplo, rellenar cierto tipo de fichero en donde hay patrones muy comunes).

En este punto surge un gran problema: no se quiere automatizar/desarrollar herramientas que faciliten dicha tarea. Sí, suena extraño y contraintuitivo, pero es así. Las razones pueden ser muchas:

  • Sería otra herramienta a mantener y no hay recursos para ello.
  • Si el que hace la herramienta se va quién lo va a mantener.
  • Todo trabajo que no sea directamente la tarea asignada es tirar el dinero.
  • No lo hacemos porque no nos lo van a agradecer (sí, no siempre es cosa de la empresa no hacerlo).

Seguro que hay más motivos, pero los anteriores los he oído más de una vez.

Es muy importante tener en cuenta que todo el tiempo que se utilice en automatizar (o simplificar) este tipo de tareas debe ser considerado inversión. Y es así porque se pueden llegar a ahorrar muchas horas de trabajo (pude hablarse, en algunos casos, de hasta cientos de horas anuales) y, por tanto, el retorno de la inversión será muy importante.

Como siempre, hay que ir con cuidado: para una tarea que se hace para un proyecto concreto que sabemos que no durará más de X tiempo puede que no compense la automatización. Por tanto hay que estudiar cada caso. Pero lo que está claro es que es necesario tener esto en cuenta.

En mi caso concreto, que estoy en desarrollo, veo como poco a poco se mejora el proceso de creación de software. Por una parte podemos escribir código menos propenso a errores, y, por otra parte, tenemos más tiempo para realizar las pruebas y ofrecer un mejor producto.

Control de errores

Uno de los grandes problemas al programar es realizar el control de los errores. Por diversos motivos me ha tocado hacer un trabajo de estudio sobre qué opciones tenemos para realizar el control de los errores de una aplicación.

La verdad es que hubiese estado muy bien hacer una tormenta de ideas al respecto, pero en estos momentos estamos todos demasiado atareados.

El siguiente texto, sin pretender ser una guía exhaustiva de lo que es el control de errores, sí busca poner un punto de partida para todo aquel que quiera ver qué mecanismos hay para este fin.

Seguir leyendo Control de errores

Tratamiento de cadenas en C

Cuando empezamos a programar en C una de las cosas que probablemente más nos cuesta controlar es el tratamiento de cadenas.

El lenguaje C es de bastante bajo nivel, por lo que sus tipos de datos básicos son muy cercanos a lo que la máquina puede utilizar. Los ordenadores no entienden de cadenas, pero sí de direcciones de memoria. De hecho, no existe un tipo de cadena, sino que se usan arrays de bytes. Y lo mejor de todo, ¡en C es el programador el que debe conocer la longitud del array! (cuanto bien, y a la vez cuánto mal, han hecho los lenguajes modernos).

Todas las funciones del estándar de C presuponen que una cadena es un array de bytes donde el último carácter es un nulo.

No voy a entrar en más detalles sobre que son las cadenas. Si quieres aprender cómo se programa con ellas puedes leer Cadenas de caracteres del manual Programación en C de  wikibooks.

Quien más y quien menos habrá visto que muchos problemas de seguridad en bibliotecas en C viene precisamente del tratamiento de cadenas. Casi se podría decir que si tienes que hacer un programa que tenga que manejar cadenas es aconsejable que mires cualquier otra cosa que no sea C. Aún así, esto no siempre será posible.

En el lugar en el que trabajo hemos empezado a cambiar el modo en que utilizamos las cadenas. Para facilitar su uso y reducir la cantidad de errores que pueden producirse, hemos creado un nuevo tipo de cadena. Bueno, se parece más a un StringBuilder de java. Las motivaciones son varias:

  • Queremos un tratamiento de cadenas más rápido (tener que recorrer una cadena cada vez que quieres conocer su longitud no es óptimo).
  • Queremos poder trabajar con cadenas sin preocuparnos por reasignar constantemente memoria. ¿Habéis probado un strcat cuando estás usando arrays y el buffer de destino es más pequeño que la cadena a concatenar?
  • Para ciertas operatorias queremos poder tener cadenas con caracteres nulos. Las cadenas de C no se llevan bien con esto, pero nosotros creamos cadenas para mandar a dispositivos que no sólo aceptan el nulo, sino que ciertas operaciones lo requieren. Con las funciones estándar sería muy complicado esto.

Personalmente no creo que seamos los primeros en hacer algo así; no tengo la más mínima duda de que hay muchas personas con los mismos problemas.

Las ventajas de usar esta biblioteca son inmediatas:

  • Simplificamos el código por no tener que preocuparnos en si hay o no espacio para almacenar la cadena y en la gestión de este espacio.
  • Minimizamos errores pues no nos pasaremos nunca del buffer (siempre podemos quedarnos sin memoria).
  • Agilizamos el desarrollo al poder escribir directamente código que haga lo que queremos (es una consecuencia del primer punto).
  • Se pueden optimizar ciertas operaciones. Esto se debe a que no hay que estar constantemente recorriendo cadenas para medirlas o que al «liberar» una cadena, no hacemos free y malloc, sino que marcamos la cadena como de longitud 0 (que es más rápido y permite aprovechar la memoria).

¡Mucho ojo con esto! Que en el día a día de mi trabajo esta haya sido una buena solución no significa que lo sea para todos los casos. Siempre hay que evaluar convenientemente cada situación antes de decantarse por una u otra solución.

Pseudo lambdas con C

Últimamente se lleva mucho el tema de las funciones lambda y los closures en los lenguajes modernos. Un ejemplo es que en C++, desde la versión de 2011, ya las incorpora. Igualmente, otros lenguajes, como Ruby, Groovy o Rust ya han sido diseñados con esta característica.

Advertencia: el código que vamos a mostrar a continuación hace uso de extensiones de GNU C. Para ser exactos, hace uso de Statement Expressions y de Nested Functions. Estas extensiones solo son soportadas por unos pocos compiladores a parte de GNU C (ver Compiler support of GNU Statement Expressions en stackoverflow).

Aunque en C no existe nada parecido es factible simularlo. Todo sea dicho, el código puede ser muy feo. En el siguiente ejemplo tenemos una función que recorre un conjunto de elementos. Esta función la llamamos proccess. Por cada elemento procesado llama a una función de callback. Lo normal es crear la función de callback a parte, lo que hace que en ciertos casos sea incómodo el mantenimiento del código.

Otra opción es declarar la función de callback in situ justo en el momento que la necesitamos:

Como puede verse, nos aprovechamos de ‘({‘ y ‘})’ para declarar un bloque de código que devuelve el valor de la última expresión para crear nuestra función de callback. Como en este caso el nombre no nos importa, la llamamos ‘_’. Lo mejor de todo es que podemos repetir este nombre sin que por ello suponga que vaya a haber conflicto en la compilación ni en la ejecución.

Actualización 1: incluimos advertencia sobre compiladores.

Agilismo mal aplicado

Recientemente una gran empresa española ha decidido dar el salto a desarrollos ágiles. Esto viene motivado por un gran proyecto que abarca una gran cantidad de áreas dentro de la empresa.

Para tener claro lo que es el agilismo, estas son los pilares sobre los que se va a apoyar la empresa para usar las metodologías ágiles:

  • La planificación es lo más importante y no puede ser cambiada de ningún modo.
  • El cliente/usuario es lo menos importante y hay que interactuar lo mínimo con él.
  • La documentación debe ser muy exhaustiva.

Hay que añadir, además, que se ha retroplanificado: primero se ha definido la fecha de fin de proyecto y a partir de ahí se planifica el resto del proyecto.

Comparemos esto con el manifiesto ágil (extraído del manifiesto por el desarrollo ágil de software):

  • Individuos e interacciones sobre procesos y herramientas
  • Software funcionando sobre documentación extensiva
  • Colaboración con el cliente sobre negociación contractual
  • Respuesta ante el cambio sobre seguir un plan

Ahora comparen; cualquier parecido es pura coincidencia. Prefiero no entrar en más detalles porque el asunto me enoja mucho (por más motivos que quizá en un futuro lejano cuente).

Curiosidades de C++

Ayer trasteando con el compilador de C++ descubrí una cosa nueva sobre cómo funciona por dentro.

Si compilamos el siguiente programa y lo ejecutamos veremos que falla con un bonito SIGSEGV:

El motivo es que, para empezar, hay dos estructuras que son diferentes pero se llaman igual. El constructor de dichas estructuras no deja de ser una función, por lo que en cada fichero objeto se generará una función para construir la estructura. Sí, la función se llamará igual en ambos objetos.

Cuando enlazamos se queda con la primera función que encuentre (en este caso con la de a.cpp). Así que cuando desde run_example_b se trata de instanciar la estructura lo hace con el constructor de a.cpp.

Hasta ayer por la tarde no caí en porque esto funciona así, pero es bastante lógico. Hay muchas clases en las bibliotecas estándar (y de terceros) cuyos constructores están implementados en la propia cabecera. Esto significa que cada fuente que tengamos tendrá su propia implementación del constructor y luego, al enlazar, se reduce a sólo uno.

Si el ejemplo anterior lo compilamos todo con ‘-O2’, el constructor lo convierte en inline para optimizar y funciona correctamente.

Nota mental: hay que mejorar los nombres de funciones in clases según su utilidad y contexto (debe ser más semántico).

Haciendo memoria

Esta semana he recordado uno de mis viejos proyectos de cuando era más joven (sería el año 97). En aquel momento tenía en mente la creación de un lenguaje que pudiese ser modificado en tiempo de compilación. Con esto me refiero a que pudiesen crearse reglas que permitieran modificar el cómo este compilaba o añadiese nuevas posibilidades al lenguaje.

En aquel momento yo no tenía conocimiento alguno de lenguajes, gramáticas o compiladores y el único curso que vi fue el de la revista Solo Programadores de varios años atrás y, la verdad, no me resolvió ninguna duda (esto se debe a que entonces no solía leer tanto y miraba solo por encima las cosas).

El caso es que me he encontrado que ya lo han inventado. En Groovy existe una característica denominada transformaciones AST que permite modificar las reglas de compilación.  Debo reconocer que es una característica tan chula como difícil de utilizar (todo hay que decirlo).

En estos momentos me encuentro estudiando la documentación para ponerme a hacer pruebas y comprobar qué tal funciona. La verdad es que permite hacer cosas tan increíbles como Groovy++, que es un optimizador de Groovy (lo que hace es eliminar algunas características de Groovy para acelerar la ejecución de las aplicaciones).

He podido encontrar una gran cantidad de ejemplo y la verdad es que me sorprenden mucho. Por ejemplo se pueden automatizar patrones de diseño con este sistema tal y como se hace con la transformación Bindable. Ésta convierte un atributo de clase en un sistema que permite monitorizar los cambios del mismo (patron ). De este modo si tenemos algo como:

1
2
3
class ClaseDeEjemplo {
    @Bindable String prop
}

Se transformará de forma automática en:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class ClaseDeEjemplo {
    private String prop
    PropertyChangeSupport pcs = new PropertyChangeSupport(this);
    public void addPropertyChangeListener(PropertyChangeListener l) {
        pcs.add(l);
    }
 
    public void removePropertyChangeListener(PropertyChangeListener l) {
        pcs.remove(l);
    }
 
    public String getProp() {
        return prop;
    }
 
    public void setProp(String prop) {
        pcs.firePropertyChanged("prop", this.prop, this.prop = prop);
    }
}

Con lo que evitamos tener que escribir un gran número de líneas de código. Sinceramente me parece un gran avance.

Muchas cosas

Ya hace tiempo que no escribo nada, sobre todo desde que uso Twitter. Pero aún así hay cosas que no va bien ponerlas en dicho servicio.

La primera de todas son mis opiniones sobre la economía. Es curioso como los estados están gastando grandes cantidades de dinero en bancos, industria automovilística y otros para mantener dichos sectores cuando la gente no consume. ¿No sería más lógico darle dicho dinero a los usuarios para que puedan realizar compras?

Lo segundo tiene que ver con la reciente sentencia sobre derechos de autor en la que se ha condenado a un webmaster por hacer uso lucrativo de descargas en internet. En este sentido me he dado cuenta que la SGAE (y otras) hace un uso lucrativo de dichas descargas gracias a la retribución por compensación de copia (el canon). Dicho de otro modo, ellos se lucran por las descargas de internet más incluso que las mafias del top manta (no tengo datos de esto, pero como ellos se los inventan, yo también).

Por último, y ya fuera de este ámbito, he estado viendo algunas cosillas de C++ que molan. El profesor Daniel nos comentó en clase sobre la plantilla future de las bibliotecas Boost C++ y me dio por implementarla yo para ver como debía funcionar. Está super chulo, ya escribiré algo sobre eso más delante.

Plantillas en C++

Recuerdo cuando empecé a aprender C++ que la cosa que menos entendía era el uso de plantillas. Debo reconocer que me ha costado mucho comprender como funciona porque era algo completamente diferente a lo que hasta entonces había visto.

Por otro lado, hemos visto en los últimos tiempos como gran cantidad de arquitecturas han copiado el modelo para implementarlo a su modo. Aquí es donde vemos los genéricos de Java y de .Net. El problema es que no tienen ni de cerca la misma potencia que el sistema de C++, pero que como ventaja consumen menos espacio de ejecutable.

Para entrar un poco en materia, comentar que las plantillas de C++ son como documentos en los que dejamos lineas en blanco y luego las rellenamos cuando ya sabemos qué queremos poner en ahí. Pero en C++ no ha líneas en blanco, estas tienen nombre.

La principal ventaja de este sistema es que permite programar clases genéricas, como pueda ser una clase lista que automáticamente se adapte al tipo que debe contener. La segunda ventaja es el rendimiento, por el mismo motivo anterior.

Para empezar a ver un poco como va la cosa vamos a ver un ejemplo sencillo de plantilla:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
template<typename T, int S>
class array
{
private:
   T *ptr;
 
public:
   array()
   {
      ptr = new T[S];
   }
 
   T& operator[](int idx)
   {
      return T[idx];
   }
 
   int size()
   {
      return S;
   }
};
//</typename>

Como vemos, hemos utilizado la palabra reservada template para indicar que se trata de una plantilla y luego, entre los símbolos < y > hemos introducido la lista de elementos de la plantilla. Estos elementos son los tipos y nombres de los valores a sustituir. En este caso son T de tipo typename y S de tipo int. Esto significa que cada vez que dentro de la clase aparezca T deberá sustituirse por su corresponditente valor pasado en la plantilla y que necesariamente deberá ser un tipo de dato (por ejemplo un int, una clase, etc.) y que cada vez que aparezca S deberá sustituirse por su valor que deberá ser un número entero. Para utilizar la plantilla sólo deberemos hacer lo siguiente:

1
2
3
4
5
6
7
8
9
10
int main(void)
{
   using namespace std;
 
   array<int , 5> a;
   cout < < "Tamaño del array: " << a.size() << endl;
   a[1] = 5;
 
   return 0;
}

Como vemos, lo único que hemos hecho ha sido añadir al nombre del la clase la lista de argumentos. En este caso concreto, deberá devolver que el tamaño es 5, que es el indicado en el parámetro de la plantilla.

Si nos fijamos en bibliotecas como STL o Boost, hacen un uso intensivo de las plantillas. El principal motivo es que C++ está optimizado para funcionar con plantillas, así que se prefieren al uso de mecanismos como la herencia. Además, la herencia hace que las llamadas a métodos sean más lentos, por lo que también se evita. Es en este momento cuando nos podemos preguntar el cómo hace C++ entonces para garantizar que se implementan ciertas funcionalidades. Por ejemplo, si queremos tener una lista ordenada, nos interesa que los elementos de la lista puedan compararse entre ellos con el operador <=. Pues la respuesta es sencilla: en la clase de plantilla simplemente utilizamos ese método y en tiempo de compilación se comprueba si está o no. Por ejemplo:

1
2
3
4
5
6
template<typename T>
T& mayor(T& a, T& b)
{
   return a >= b ? a : b;
}
//</typename>

Hemos creado una función mayor que devuelve el mayor de dos elementos de tipo T. Los elementos de tipo T deben sobrecargar el operador >= para que funcione. Como se ve, no es necesario hacer herencia de ningún tipo, el compilador detecta automáticamente si está o no sobrecargado y dará el correspondiente error en caso necesario (si miramos el caso de Java o de .Net veremos como sí es necesario hacer herencia para que el mecanismo de genéricos funcione de este modo).

Debo decir que el tema de las plantillas está muy bien y se puede hacer muchas cosas interesantes, como la clase array, que funciona igual que un array y que tiene un rendimiento muy parecido (benditos métodos inline de C++).

Otro día, seguiré hablando sobre plantillas, que dan para mucho.