name: title class: left, top background-image: url(https://danlebrero.com/images/blog/books/grokking-simplicity/grokking-simplicity.jpg) background-size: cover # .fancy[[TE] Functional thinking] ### Functional programming <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> .whiteinline[.left[Carlos Granell· Universitat Jaume I]] .whiteinline[.left[EI1039 - Diseño de Software · Sep 2023]] ??? Image credits: [danlebrero.com](https://danlebrero.com/2021/09/15/grokking-simplicity-summary/) [How Functional Programming Can Make You A Better Developer](https://www.youtube.com/watch?v=EqO4TcNLjl0) [Book notes: Grokking Simplicity](https://danlebrero.com/2021/09/15/grokking-simplicity-summary/) --- class: left ### Paradigmas de programación .large[
PROCEDURAL] -- .large[
ORIENTADA A OBJETOS] > .large[Abstrae entidades del mundo real en objetos] > .large[Organiza el acceso a los datos] -- - .large[
**encapsulación**: variables, objetos, clases...] -- - .large[
**herencia**: objetos complejos se crean extendiendo otros más simples] --- class: left ### Paradigmas de programación .large[
FUNCIONAL .small[<a name=cite-normand2021></a>[[Nor21](#bib-normand2021)]] ] .large[Cada lenguaje de programación tiene funciones, entonces, ¿qué hace que un lenguaje de programación sea **funcional**?] -- > .large[Salidas solo dependen de las entradas] > .large[Sin efectos laterales! *No side effects!*] -- - .large[
**acciones vs cálculos**: código organizado en las partes que pueden tener efectos laterales y en las que no] -- - .large[
técnicas para **procesar colecciones de datos** (arrays, lists,...): "todos a la vez" en vez de elemento a elemento] - Funcionan mejor si siguen **no side effects!** ??? Techniques are most effective when items can be processed independently, free of side effects, so they work better thanks to **no side effects**. --- name: map-function class: left # `map` en R .small[<a name=cite-wickham2019></a><a name=cite-altman2021></a>[[Wic19](https://adv-r.hadley.nz/index.html); [ABW21](https://dcl-prog.stanford.edu/)]] ```r # Cada vector es un planeta. # Elementos de cada vector representan el radio (km) de las lunas del planeta. moons <- list( earth = 1737.1, mars = c(11.3, 6.2), neptune = c(60.4, 81.4, 156, 174.8, 194, 34.8, 420, 2705.2, 340, 62, 44, 42, 40, 60) ) ``` -- ```r length(moons) ## [1] 3 ``` -- ```r length(moons$earth) ## [1] 1 length(moons$mars) ## [1] 2 length(moons$neptune) ## [1] 14 ``` ??? [https://dcl-prog.stanford.edu/purrr-basics.html](https://dcl-prog.stanford.edu/purrr-basics.html) from [Functional Programming](https://dcl-prog.stanford.edu/) --- class: left # `map` en R <img src="https://dcl-prog.stanford.edu/images/map-step-1.png" width="30%" style="display: block; margin: auto;" /> ??? `map()`, like all the map functions, takes a list/vector and a function as arguments. --- class: left # `map` en R <img src="https://dcl-prog.stanford.edu/images/map-step-2.png" width="60%" style="display: block; margin: auto;" /> ??? `map()` then applies that function to each element of the input list/vector. --- class: left # `map` en R <img src="https://dcl-prog.stanford.edu/images/map.png" width="60%" style="display: block; margin: auto;" /> ??? Applying the function to each element of the input list/vector results in one output element for each input element. --- class: left # `map` en R <img src="https://dcl-prog.stanford.edu/images/map-output.png" width="80%" style="display: block; margin: auto;" /> ??? `map()` then combines all these output elements into a list. --- class: left # `map` en R .large[
Aplicamos la función `length()` a cada elemento de la lista de entrada `moons`] <img src="https://dcl-prog.stanford.edu/images/map-length.png" width="80%" style="display: block; margin: auto;" /> --- class: left # `map` en R .large[
Cada invocación de `length()` produce un entero, luego el resultado es una lista de enteros] <img src="https://dcl-prog.stanford.edu/images/length-list.png" width="80%" style="display: block; margin: auto;" /> --- class: left # `map` en R ```r map(moons, length) ## $earth ## [1] 1 ## ## $mars ## [1] 2 ## ## $neptune ## [1] 14 ``` --- class: left # `map` en R .panelset.sideways[ .panel[.panel-name[`sort()`] ```r sort(moons$neptune) ## [1] 34.8 40.0 42.0 44.0 60.0 60.4 62.0 81.4 156.0 174.8 ## [11] 194.0 340.0 420.0 2705.2 ``` ] .panel[.panel-name[`map()` + `sort`] ```r map(moons, sort) ## $earth ## [1] 1737.1 ## ## $mars ## [1] 6.2 11.3 ## ## $neptune ## [1] 34.8 40.0 42.0 44.0 60.0 60.4 62.0 81.4 156.0 174.8 ## [11] 194.0 340.0 420.0 2705.2 ``` ] .panel[.panel-name[`sort(decreasing)`] ```r sort(moons$neptune, decreasing = TRUE) ## [1] 2705.2 420.0 340.0 194.0 174.8 156.0 81.4 62.0 60.4 60.0 ## [11] 44.0 42.0 40.0 34.8 ``` ] .panel[.panel-name[`map()` + `sort` + `decreasing`] ```r map(moons, sort, decreasing = TRUE) ## $earth ## [1] 1737.1 ## ## $mars ## [1] 11.3 6.2 ## ## $neptune ## [1] 2705.2 420.0 340.0 194.0 174.8 156.0 81.4 62.0 60.4 60.0 ## [11] 44.0 42.0 40.0 34.8 ``` ] ] --- class: left # `map` en R .large[
Argumentos extra a `map()` se pasan automáticamente a la función] <!-- <img src="https://dcl-prog.stanford.edu/images/map-extra-arg.png" width="80%" style="display: block; margin: auto;" /> --> <img src="https://d33wubrfki0l68.cloudfront.net/e1b3536a7556aef348f546a79277125c419a5fdc/0c0a1/diagrams/functionals/map-arg.png" width="70%" style="display: block; margin: auto;" /> ??? Any arguments that come after `f` in the call to `map()` are inserted after the data in individual calls to `f()`. [Source](https://adv-r.hadley.nz/functionals.html) --- class: left # `map` en R .large[
Argumentos extra a `map()` se pasan automáticamente a la función] <img src="https://d33wubrfki0l68.cloudfront.net/a468c847ea8aca9a6131492e1e7431f418259eaf/ce4e0/diagrams/functionals/map-arg-recycle.png" width="70%" style="display: block; margin: auto;" /> ??? It’s important to note that these these arguments are not decomposed; or said another way, `map()` is only vectorised over its first argument. If an argument after `f` is a vector, it will be passed along as is. [Source](https://adv-r.hadley.nz/functionals.html) --- class: inverse, center, middle # ¿Por qué son tan eficcientes estas funciones (`map()`, etc.)? --- class: inverse, center, middle # .cold[Sin efectos laterales!] # .cold[*No side effects!*] --- class: inverse, center, middle # .cold[El principio de la programación funcional es...] --- class: inverse, center, middle # .cold[...saber diferenciar entre ACCIONES y CÁLCULOS] > .right[.large[.fancy["El código siempre se organiza en acciones, cálculos y datos"]]] .right[*cualquier programador funcional*] --- class: center, middle # Si comprendes esta idea en PF para gestionar los efectos laterales, la podrás aplicar a CUALQUIER lenguaje de programación --- class: inverse, center, middle # Acciones, cálculos y datos --- background-image: url(https://drek4537l1klr.cloudfront.net/normand/Figures/f0006-01.jpg) background-size: contain ??? Image credits: [https://livebook.manning.com/book/grokking-simplicity/chapter-1/1](https://livebook.manning.com/book/grokking-simplicity/chapter-1/1) --- background-image: url(https://drek4537l1klr.cloudfront.net/normand/Figures/f0007-01.jpg) background-size: contain ??? Image credits: [https://livebook.manning.com/book/grokking-simplicity/chapter-1/1](https://livebook.manning.com/book/grokking-simplicity/chapter-1/1) --- background-image: url(https://drek4537l1klr.cloudfront.net/normand/Figures/f0008-01.jpg) background-size: contain ??? Image credits: [https://livebook.manning.com/book/grokking-simplicity/chapter-1/1](https://livebook.manning.com/book/grokking-simplicity/chapter-1/1) --- name: actions class: middle # .coldinline[Acciones] > .large[Dependen de cuántas veces o cuándo se ejecutan] ## También conocidas como... > .large[Funciones con efectos laterales, funciones impuras] ## Ejemplos > .large[Enviar un correo, leer de una base de datos] ??? Si envío un correo electrónico urgente hoy, es muy diferente a enviarlo la próxima semana. Y enviar el mismo correo electrónico 10 veces es diferente de enviarlo 1 vez o ninguna.] --- name: calculations class: middle # .coldinline[Cálculos] > .large[Producen la misma salida dada la misma entrada. No dependen de cuándo ni cuántas veces se invocan] ## También conocidas como... > .large[Funciones puras] ## Ejemplos > .large[Comprobar si dirección email es correcta, concatenación de cadenas] --- name: data class: middle # .coldinline[Datos] > .large[Son hechos sobre algo. Inertes, no son ejectables] > .large[Deben interpretarse para ser útiles] ## También conocidas como... > .large[Hechos] ## Ejemplos > .large[Dirección de correo de un usuario, la ubicación devuelta por un servicio de geocoding, lugares de origen y destino] ??? Ejemplo: el recibo de un restaurante: el gerente del restaurante puede usarlo para determinar qué alimentos son populares. Y el cliente lo puede utilizar para realizar un seguimiento de su presupuesto para salir a cenar. --- class: inverse, center, middle ## .cold[En PF, se prefieren los datos a los cálculos y los cálculos a las acciones] --- class: inverse, center, middle # Extraer cálculos de acciones --- background-image: url(https://drek4537l1klr.cloudfront.net/normand/Figures/f0062-01.jpg) background-size: contain # Megamart.com --- # Megamart.com ```javascript var shopping_cart = []; var shopping_cart_total = 0; ``` ```javascript function add_item_to_cart(name, price) { shopping_cart.push({ name: name, price: price }); calc_cart_total(); } ``` ```javascript function calc_cart_total() { shopping_cart_total = 0; for(var i = 0; i < shopping_cart.length; i++) { var item = shopping_cart[i]; shopping_cart_total += item.price; } set_cart_total_dom(); update_shipping_icons(); /* lo vemos luego */ update_tax_dom(); /* lo vemos luego */ } ``` --- background-image: url(https://drek4537l1klr.cloudfront.net/normand/Figures/f0063-01.jpg) background-size: contain # Megamart.com --- # Megamart.com ```javascript function update_shipping_icons() { var buy_buttons = get_buy_buttons_dom(); for(var i = 0; i < buy_buttons.length; i++) { var button = buy_buttons[i]; var item = button.item; if(item.price + shopping_cart_total >= 20) button.show_free_shipping_icon(); else button.hide_free_shipping_icon(); } } ``` ```javascript function update_tax_dom() { set_tax_dom(shopping_cart_total * 0.10); } ``` --- # Todo el código anterior son acciones .panelset.sideways[ .panel[.panel-name[`variables`] ### Acción: var globales son mutables ```javascript var shopping_cart = []; var shopping_cart_total = 0; ``` ] .panel[.panel-name[`add_item_to_cart`] ### Acción: modifica var globales ```javascript function add_item_to_cart(name, price) { shopping_cart.push({ name: name, price: price }); calc_cart_total(); } ``` ] .panel[.panel-name[`update_shipping_icons`] ### Acción: lee del DOM; modifica el DOM ```javascript function update_shipping_icons() { var buy_buttons = get_buy_buttons_dom(); for(var i = 0; i < buy_buttons.length; i++) { var button = buy_buttons[i]; var item = button.item; if(item.price + shopping_cart_total >= 20) button.show_free_shipping_icon(); else button.hide_free_shipping_icon(); } } ``` ] .panel[.panel-name[`update_tax_dom`] ### Acción: modifica el DOM ```javascript function update_tax_dom() { set_tax_dom(shopping_cart_total * 0.10); } ``` ] .panel[.panel-name[`calc_cart_total`] ### Acción: modifica var global ```javascript function calc_cart_total() { shopping_cart_total = 0; for(var i = 0; i < shopping_cart.length; i++) { var item = shopping_cart[i]; shopping_cart_total += item.price; } set_cart_total_dom(); update_shipping_icons(); update_tax_dom(); } ``` ] ] --- class: center, middle # ¿Es código testeable y reutizable? --- # Extraemos cálculos de acciones .panelset.sideways[ .panel[.panel-name[`Original`] ### `calc_cart_total()` ```javascript function calc_cart_total() { shopping_cart_total = 0; for(var i = 0; i < shopping_cart.length; i++) { var item = shopping_cart[i]; shopping_cart_total += item.price; } set_cart_total_dom(); update_shipping_icons(); update_tax_dom(); } ``` ] .panel[.panel-name[Paso 1] ### Aislar código que *calcula* en nueva función ```javascript function calc_cart_total() { * calc_total(); set_cart_total_dom(); update_shipping_icons(); update_tax_dom(); } *function calc_total() { * shopping_cart_total = 0; * for(var i = 0; i < shopping_cart.length; i++) { * var item = shopping_cart[i]; * shopping_cart_total += item.price; * } *} ``` ] .panel[.panel-name[Paso 2] ### Identificar inputs/outputs *explícitos y implícitos* ```javascript function calc_cart_total() { calc_total(); set_cart_total_dom(); update_shipping_icons(); update_tax_dom(); } function calc_total() { * shopping_cart_total = 0; # Output implícito * for(var i = 0; i < shopping_cart.length; i++) { # Input implícito * var item = shopping_cart[i]; # Input implícito * shopping_cart_total += item.price; # Output implícito } } ``` ] .panel[.panel-name[Paso 3] ### Convertir outputs *implícitos* en *explícitos* ```javascript function calc_cart_total() { * shopping_cart_total = calc_total(); set_cart_total_dom(); update_shipping_icons(); update_tax_dom(); } function calc_total() { * var total = 0; # Output explícito for(var i = 0; i < shopping_cart.length; i++) { # Input implícito var item = shopping_cart[i]; # Input implícito * total += item.price; # Output explícito } * return total; # Output explícito } ``` ] .panel[.panel-name[Paso 4] ### Convertir inputs *implícitos* en *explícitos* ```javascript function calc_cart_total() { * shopping_cart_total = calc_total(shopping_cart); set_cart_total_dom(); update_shipping_icons(); update_tax_dom(); } *function calc_total(cart) { # Input explícito var total = 0; # Output explícito * for(var i = 0; i < cart.length; i++) { # Input explícito * var item = cart[i]; # Input explícito total += item.price; # Output explícito } return total; # Output explícito } ``` ] .panel[.panel-name[Fin] ### `calc_total()` ya es un cálculo! ```javascript function calc_cart_total() { shopping_cart_total = calc_total(shopping_cart); set_cart_total_dom(); update_shipping_icons(); update_tax_dom(); } *function calc_total(cart) { var total = 0; for(var i = 0; i < cart.length; i++) { var item = cart[i]; total += item.price; } return total; } ``` ] ] --- class: left ### Resumen: extracción de cálculos a partir de acciones .large[
Funciones que son acciones tienen inputs y/o /outputs implícitos] -- .large[
Cálculos no tienen inputs y/o outputs implícitos] -- .large[
Variables compartidas son inputs/outputs implícitos] -- .large[
Inputs implícitos se pueden reemplazar por argumentos de entrada] -- .large[
Outputs implícitos se pueden reemplazar por valores de salida] --- ### Resumen .large[
En PF, **datos y cálculos prevalecen sobre acciones**] - .large[lecturas de datos mutables son acciones] - .large[lecturas de datos inmutables son cálculos] -- .large[
Los cálculos son fáciles de .coldinline[**TESTEAR**]] -- .large[
Lisp, Haskell, Clojure...son lenguajes funcionales] -- .large[
Java, JS, Python, R... no son lenguajes funcionales puros, pero adoptan ideas de la programación funcional. .coldinline[**¡Piensa funcional!**]] -- .large[
La función `f` para un `map(x,f)`: ¿Acción o cálculo?] --- # Referencias <a name=bib-wickham2019></a>[Wickham, Hadley](#cite-wickham2019) (2019). _Advanced R_. ISBN: 978-0815384571. URL: [https://adv-r.hadley.nz/index.html](https://adv-r.hadley.nz/index.html). <a name=bib-altman2021></a>[Altman, Sara, Bill Behrman, et al.](#cite-altman2021) (2021). _Functional Programming_. URL: [https://dcl-prog.stanford.edu/](https://dcl-prog.stanford.edu/). <a name=bib-normand2021></a>[Normand, Eric](#cite-normand2021) (2021). _Grokking Simplicity: Taming complex software with functional thinking_. ISBN: 978-1617296208.