Haskell, C# y Currying
Aunque el concepto de “Currying” no es algo extraño para los lenguajes funcionales (y por lo tanto bastante conocido entre usuarios de este tipo de lenguajes). No es sólo una palabra extraña sino un concepto quizás desconocido para muchos usuarios de lenguajes imperativos como C#.
Currying (llamado así en nombre de Haskell Curry) no es más que el efecto de tomar de forma individual parámetro por parámetro y procesar cada parámetro por separado en su propia función, esto lo logramos tranformando el método o función en cuestión por uno que tome uno de los parámetros y una función para luego retornar otra función. Por ejemplo, tomemos el hecho de una simple suma:
sum :: (Int, Int) -> Int sum (x, y) = x + y
En este caso indicamos una función llamada sum que toma dos parámetros de tipo Int y retorna un parámetro de tipo Int. Dado que las funciones son ciudadanos de primer orden en los lenguajes funcionales, podemos transformarla y definir una función que tome como uno de sus parámetros una función y retorne otra función
sum' :: Int -> Int -> Int sum' x y = x + y
En este caso, creamos la función sum’ que toma un parámetro tipo Int y retorna otra función que toma un parámetro tipo Int y retorna un Int. Si, ya se que a simple vista esto puede parecer extraño, pero regresemos a ver un par de puntos claves: Currying involucra evaluar uno a uno los parámetros de una función pudiendo retornar otra función en su lugar.
Bien, regresemos a la Tierra y veamos como podemos transformar este tipo de cosas en una forma más "C#". Aprovechando los métodos lambda en C# (otra adición netamente funcional) podemos describir algo como esto:
public static void Main() { Func<int, int, int> sum = (x, y) => x + y; int result = sum(1,2); Console.WriteLine(result); }
Pero que les parecería si cambiamos "un poco" la definición de la función sum:
public static void Main() { Func<int, Func<int, int>> sum = x => y => x + y; int result = sum(1)(2); Console.WriteLine(result); }
Ahora sum en vez de tomar dos enteros como parámetro y retornar un entero, ahora toma un entero retorna una función (que toma un entero y retorna otro entero) como parámetros. De esta manera sum(1) retornará otra función que luego toma el parámetro 2 y retorna 3, wow...
El concepto de currying es práctico, permite definir funciones complejas a partir de funciones mucho más simples, aunque la definición en C# parece compleja, en realidad permite definir una función a partir de simplemente un parámetro (el cual convenientemente puede permanecer dentro de la función como si fuera una constante).
public static void Main() { Func<int, Func<int, int>> ntimes = x => y => x * y; var twice = ntimes(2); var three = ntimes(3); Console.WriteLine(twice(4)); Console.WriteLine(three(4)); }
Es interesante todo lo que podríamos lograr con solamente un poco de imaginación. :)
Note: Cross posted from IDisposable Thoughts.Permalink