Programación funcional en CSharp, algunas pinceladas:

 

Sí, es cierto que si tenemos que clasificar a CSharp en un paradigma, este sería orientado a objetos. Pero desde .NET 3.5 con la inclusión de LINQ, o incluso antes, en .NET 2.0 (si no recuerdo mal) con la inclusión de los genéricos, el lenguaje ha ido añadiendo más componentes del paradigma funcional. En este post voy a intentar describir algunas (y sólo algunas) de las ventajas del paradigma funcional aplicado a CSharp, sin intención de sembrar cátedra.



Funciones de orden superior

Una función de orden superior grosso modo es una función que acepta como parámetro otra función. Esto lo podemos ver de LINQ. Una de las sobrecargas de lacláusulaa Where acepta una función de parámetro T y que devuelve un booleano.

Una de las mejores formas de añadirlas en nuestro código, es precisamente como lo implementa Linq. Es decir, a través de métodos extensores.


public static class FooExtensions{

public static void DoFoo(this Foo foo, Action DoSomething){

DoSomething();

}

}


Otro punto muy interesante de las métodos extensores es que nos facilitan la vida para crear apis fluidas, ahí ya entramos en el siguiente punto.


Expresividad


Aunque la expresividad no esté catalogada como característica de un lenguaje funcional, sí que es cierto que suelen ser más expresivos. En esto las apis fluent tienen mucho que decir, ya que pasamos de hacer las cosas en un modo imperativo (ordenando) a uno más declarativo (pidiendo).


Suele considerarse buena práctica definir constantes para hacer nuestro código más expresivo cuando trabajamos con cadenas. ¿Por qué no hacer lo mismo con funciones? Este es un ejemplo de código sacado de un proyecto real en el que estoy trabajando:


SettingsViewModel.ShowWebCommand.Execute(url);


No parece muy ofensivo, simplemente ejecuta un comando desde una vista (iOS) a un ViewModel (MvvmCross) que no puede ser bindado por características de implementación. Lo que es seguro es que si tienes que escribirlo varias veces, como es el caso, acaba siendo un poco desagradable a la vista. ¿Cómo hacerlo más conciso? Simplemente,asignándole a una función que recibe como parámetro un string y no devuelve nada:


Action<string> OpenWeb = (url) => SettingsViewModel.ShowWebCommand.Execute(url);


Las llamadas serían OpenWeb(url);


Mucho más simple no? Seguro que hay casos donde se maximiza aún más la expresividad.


Inmutabilidad

La inmutabilidad es una de las características más importante de los lenguajes funcionales. Es en realidad un concepto muy simple, viene a decir que las variables no almacenan estado (la mayoría de bugs suelen ser problemas de estado). Esto quiere decir que no asignamos valores a variables, en su lugar se definen símbolos que no podrán ser cambiados a posteriori. En CSharp trabajamos con variables en lugar de símbolos, por lo que en principio no podemos hacer el mismo procedimiento. Pero sí que es posible marcar variables como inmutables con readonly, e incluso hay librerías con colecciones inmutables out of the box.


Sinceramente, creo que la mejor forma de evitar el estado, sobre todo en el dominio (en UI y BBDD evidentemente es inevitable) es intentar reducir tanto como sea posible las declaraciones de variables a nivel de clase. Y si lo hacemos, modificarlas o bien con funciones void que prácticamente sólo hagan dicha modificación o bien si estamos construyendo una api fluent que se reciba como parámetro dicha variable, se modifique y se devuelva (aunque entonces no tendría mucho sentido almacenar el estado de la variable en cuestión :P)



Clousures


Un Clousure, aunque suene cool, no es más que una función definida en un ámbito en el que hay variables en el mismo ámbito, a las cuales la función podría acceder. Más claro aún:


void SomeFunction(){

var whatever = new Whatever();

var foo = new Foo();

foo.GetAllFoos().Where(f=>f.whatever==whatever);

}


El clousure sería el predicado del where, porque la estamos declarando en el ámbito de Whatever y por tanto, podemos acceder a Whatever. Pero si en lugar de definir el Where ahí, usaramos un Func<Foo,bool> previamente definido (como el action openWeb anterior), ya no sería un Clousure. Hay que tenercuidadoo con los Clousures si se usan en un contexto iterativo, porque al usar la variable del mismo ámbito, ésta tendrá el valor de la última iteración siempre (si es que cambia).



Continuará

No sé cuándo, pero escribiré otra entrada sobre esto con algún puñado de conceptos más. Espero que haya resultado útil a alguien. 🙂

2 Comentarios

  1. Felicidades por el artículo, muy buena introducción a la programación funcional con C#. Esperamos el próximo con ansias!

    Salu2!

  2. jmgomez

    14 Octubre, 2013 at 1:10 pm

    Muchas gracias Miguel!

Deja un comentario

Tu dirección de correo electrónico no será publicada.

*