Spanish (Español) translation by Andrea Jiménez (you can also view the original English article)
Introducción
Currying es una característica que se encuentra en la mayoría de los lenguajes de programación modernos. Traduce una sola función con varios argumentos en una serie de funciones, cada una con un argumento. En esencia, esto permite almacenar funciones en variables y crear funciones que retornen funciones.
Aunque al principio puede parecer un concepto extraño, es una técnica poderosa que en ocasiones puede resultar muy útil. En este tutorial, te mostraré cómo aprovechar la función currying en Swift.
1. Función Currying con clases
Antes de definir nuestras propias funciones personalizadas, primero voy a mostrarte un ejemplo sencillo de currying en Swift usando las clases.
Abre Xcode, crea una nueva configuración para iOS u OS X y agrégalo al siguiente código:
class Car { var speed = 0 func accelerateBy(factor: Int) -> Int { speed += factor return speed } } let car1 = Car()
Definimos una clase básica con una propiedad y un método o función de instancia. También creamos una instancia de la clase, car1
. Con el siguiente código podemos llamar al método accelerateBy(_:)
de nuestra instancia Car
:
car1.accelerateBy(10)
Sin embargo, hay otra forma de ejecutar este método mediante el uso de la función currying. En el ejemplo anterior, llama al método directamente en la instancia car1
a pesar de que el método está realmente definido en la clase Car
. El método que hemos escrito no es específico de la instancia car1
, sino de la clase Car
. Cuando llamas a este método, lo que realmente está sucediendo es que Swift primero va a la clase Car
, recupera el método accelerateBy(_:)
, le informa al método qué instancia se está utilizando y luego ejecuta un método modificado específico para esa instancia. Para mostrarte cómo funciona esto, agrega la siguiente línea a tu configuración:
Car.accelerateBy(car1)
Lo que estamos haciendo aquí es acceder al método accelerateBy(_:)
de la clase Car
, y pasar como parámetro en la instancia que queremos que se ejecute esta función. En la barra lateral de tu configuración, puedes ver que el resultado de este método es otra función.



Lo que representa este resultado de la (Función) es en realidad un nuevo método accelerateBy(_:)
específico para la instancia car1
. Esta función retornada es única porque hace referencia específicamente a la propiedad speed
de la instancia car1
en lugar del método genérico que definiste anteriormente, que puede hacer referencia a la propiedad speed
de cualquier instancia de Car
.
Como era de esperar, podemos pasar los nuevos parámetros a esta función retornada con el fin de ejecutarla como lo hacemos normalmente. Agrega la siguiente línea de código a tu configuración:
Car.accelerateBy(car1)(10)
Con este código, pasamos 10
como parámetro al método exclusivo accelerateBy(_:)
y obtenemos como resultado la velocidad actual de car1
.



¡Felicitaciones! Acabas de aprovechar la función currying en Swift por primera vez.
Además de combinar varias funciones, el resultado de la función que retorna tu código también se puede almacenar en una variable. Esto te permite almacenar y reutilizar rápidamente el método accelerateBy(_:)
específico para tu instancia car1
. Agrega las siguientes líneas a tu configuración:
let a = Car.accelerateBy(car1) a(10) a(20) a(30)
Ahora puedes ver que la variable a
se comporta como cualquier otra función definida globalmente. La ventaja es que es específico para una instancia de Car
en particular, que se puede definir en tiempo de ejecución en lugar de en tiempo de compilación. En tu configuración, puedes ver las salidas esperadas que se muestran en la barra lateral.



Por último, te mostraré cómo puedes retornar una función desde otra función reimplementando el comportamiento de la clase Car
. Vamos a hacer esto definiendo una función que retorne otra función. Esta función podría verse así en tu código:
func someFunction(input: AnyObject) -> (AnyObject) -> AnyObject { // Do stuff to return a function }
Definimos la función someFunction(_:)
, la cual acepta un parámetro AnyObject
y retorna otra función que también acepta un parámetro AnyObject
que retorna un resultado AnyObject
. Este tipo de definición de la función puede parecer muy confuso y abrumador al principio. Para simplificarlo, podemos aprovechar la palabra clave typealias
en Swift para asignar un nuevo nombre a cualquier tipo de datos.
Agrega el siguiente código a tu configuración:
typealias IntFunction = (Int) -> Int func accelerationForCar(car: Car) -> IntFunction { return Car.accelerateBy(car) } let newA = accelerationForCar(car1) newA(10)
Al usar typealias
para asignar el nombre de IntFunction
al tipo de datos (Int) -> Int
, hemos simplificado enormemente la definición de la función accelerationForCar(_:)
. El tipo de datos (Int) -> Int
simplemente representa una función que acepta un parámetro Int
y retorna un valor Int
.
Esta nueva función utiliza el comportamiento integrado de la clase Car
para retornar un objeto IntFunction
que, luego, puede almacenarse en una variable y usarse como lo hicimos antes.
2. Funciones personalizadas de currying
Es posible que quieras crear tus propias funciones que no estén relacionadas con las clases. Aunque el comportamiento de la función currying integrada en Swift es muy útil. En esta parte del tutorial, vamos a usar el ejemplo de una función que multiplica otro número por un valor constante.
Imagina que quieras crear una función que acepte un solo número de entrada y lo multiplique por 5. Esta función simple podría verse así:
func multiplyBy5(a: Int) -> Int { return a * 5 }
Ahora imagina que necesitas una función similar, pero necesitas que se multiplique por 10 en lugar de 5. Luego necesitas otra función para multiplicar por 20. Si bien puedes crear tres funciones similares y nombrarlas multiplyBy5
, multiplyBy10
y multiplyBy20
, esto podrías manejarlo mucho mejor usando la función currying.
Agrega el siguiente fragmento de código a tu configuración:
func multiplyBy(a: Int) -> IntFunction { func nestedMultiply(b: Int) -> Int { return a * b } return nestedMultiply } multiplyBy(10)(20) let multiplyBy5 = multiplyBy(5) multiplyBy5(4)
Definimos la función multiplyBy(_:)
que acepta un Int
como su único parámetro y retorna una función de tipo IntFunction
, el tipo de datos que definimos anteriormente en este tutorial. En esta función, definimos otra función, nestedMultiply(_:)
. Anidamos esta función dentro de la primera para que no se pueda ejecutar fuera del alcance de la función multiplyBy(_:)
y tenga acceso al parámetro de entrada a
.
Las tres líneas debajo de la definición de la función son ejemplos simples de cómo puedes combinar las funciones de curry.



Aunque puedes crear funciones para currying utilizando funciones anidadas, también puedes crearlas mediante cierres o definiendo varios conjuntos de parámetros. Como ejemplo, agrega el siguiente fragmento de código tu configuración:
func add(a: Int) -> IntFunction { return { b in a + b } } let add2 = add(2) add2(4) func subtract(a: Int)(b: Int) -> Int { return b - a } let subtract5 = subtract(5) subtract5(b: 8)
Como puedes ver, estas dos nuevas funciones se pueden combinar de la misma manera que todas las demás funciones a lo largo de este tutorial. La única excepción es que, con la función definida con dos conjuntos de parámetros, debes asignar explícitamente el nombre del segundo parámetro.
No hay límite en el número de niveles de la función currying que puedes implementar dentro de tu código. El siguiente código te muestra un ejemplo de tres funciones curry y cómo se diferencia de una sola función con tres parámetros.
func multiply(a: Int, b: Int, c: Int) -> Int { return a * b * c } func multiply(a: Int) -> (Int) -> IntFunction { func nestedMultiply1(b: Int) -> IntFunction { func nestedMultiply2(c: Int) -> Int { return a * b * c } return nestedMultiply2 } return nestedMultiply1 } multiply(4, 5, 6) multiply(4)(5)(6)
La principal diferencia entre estas dos funciones de multiplicación es que la versión curry se puede pausar y almacenar de manera efectiva en una variable después de que se haya procesado el primer o segundo parámetro. Esto hace que la función sea muy fácil de reutilizar y pasar como un objeto.
Conclusión
La función de currying en Swift es un concepto difícil de comprender, pero, en esencia, se trata básicamente de dos conceptos clave:
- La creación de funciones que retornan otras funciones.
- Reducir funciones con múltiples argumentos en una serie de funciones, cada una con un argumento.
Hay varias formas de combinar las funciones y, aunque en este tutorial solo usamos el tipo de datos Int
, se pueden usar los mismos procesos con cualquier tipo de datos en tus proyectos Swift. Esto permite que las funciones se almacenen en las variables y se usen muchas veces en todo el código.
Como siempre, asegúrate de dejar tu opinión y sugerencias en la zona de los comentarios.