La idea principal en programación funcional es llevar a cabo el proceso mediante la aplicación de funciones sin guardar estado (sin modificar los valores originalmente proveídos). Por ello, y al igual que en álgebra, es posible crear una función compuesta, que no es nada más que la aplicación sucesiva de dos funciones. Obviamente, hay que tener cuidado en que el valor de retorno de la primera función sea del tipo del argumento que la segunda función espera.
Wikipedia muestra una imagen que ilustra claramente el concepto de composición de funciones.
La composición de funciones se define con el signo ・, que indica que la primera función se aplica al resultado de aplicar la segunda función al parámetro recibido. Usando el ejemplo de la imagen y definiendo h = g ・f, tenemos que h(a) = @, puesto que h se define aplicando g(f(a)).
El mismo concepto se aplica en programación funcional. Supongamos que tenemos las siguientes funciones:
def toInt(s: String) = s.toInt def addOne(i: Int) = i + 1 def by4(i: Int) = i * 4
Scala tiene 2 operadores para componer funciones: compose y andThen. La diferencia es que mientras f compose g es f(g(x)), f andThen g es g(f(x)). Por tanto, si queremos definir una función que primero convierta la cadena recibida en entero y después le sume uno a ese entero, podemos hacerlo de dos formas:
/* With compose: composed1 = addOne(toInt(x)) */ val composed1 = addOne _ compose toInt /* With andThen composed2 = addOne(toInt(x)) */ val composed2 = toInt _ andThen addOne
Es entonces fácil ver que estas 2 funciones compuestas (composed3 y composed4) no son lo mismo:
val composed3 = addOne _ compose by4 val composed4 = addOne _ andThen by4
Aplicando las funciones compuestas definidas:
val strFunctions = List(composed1, composed2) val intFunctions = List(composed3, composed4) strFunctions foreach (f => println(f("3"))) intFunctions foreach (f => println(f(4)))
Y los resultados son:
4 4 17 20
El concepto de composición de funciones es realmente muy simple, y facilita mucho la creación de nuevas funciones basadas en algunas que ya tengamos definidas.
Como nota adicional, en Haskell es mucho más fácil crear una función compuesta, ya que solo es necesario usar “.”:
import Control.Monad main = do f ← liftM(composed1) $ getLine print f where composed1 = addOne . toInt addOne ∷ Int → Int addOne = (+1) toInt ∷ String → Int toInt x = read x ∷ Int
El “.” en Haskell funciona como el compose en Scala; además, hay diferentes formas de hacer un programa como el de arriba, pero en este caso escogí usar liftM.
Cabe mencionar que en Scala también es posible componer 2 funciones monádicas en una usando algo llamado composición Kleisli, pero hay que usar Scalaz para tener acceso a ella. Y no es necesario entender teoría de categorías para usarlas; simplemente hay que cuidar los parámetros y los tipos de datos que regresan las funciones. Esto es tema de otro post, pero lo pongo aquí por si alguien se interesa pueda ir investigando por su cuenta.
@medinamanuel Muy buen Tweet 🙂 Intereresante info!
RT @medinamanuel: Nuevo post en ¡Un mexicano en Japón! 「Composición de funciones en Scala」 http://t.co/7DU1y8d52v
Composición de funciones en Scala http://t.co/4rfIj9s5NJ
>@medinamanuel Composición de funciones en Scala: La idea principal en programación funcional es llevar a cabo… http://t.co/bniAAbnJoB
Kazue Betty Kobayashi liked this on Facebook.