Programación funcional para el día a día

Servidora        

 

@HenarMendiola

Servidora        

 

  • Doctora en Física de sistemas complejos
  • Desarrolladora en eldiario.es

@HenarMendiola

Comunidades

¿Porqué esta charla?    

 

Composición

 

Recursividad

 

Mónadas

 

Funtores

 

Efectos Secundarios

 

Inmutabilidad

 

@HenarMendiola

OutLine      

 

  1. ¿Porqué y para qué?
  2. ¿Qué es la PF?
  3. Una aplicación totalmente funcional... o casi
    • Librerías
    • Intenciones simples
    • Intenciones Complejas
    • Contextos seguros
    • Efectos secundarios
    • Reutilización
  4. Resumiendo
    • Conclusiones
    • Bibliografía

@HenarMendiola

¿Qué es PF?    

 

  • Declarativa
  • Funciones puras
  • Determinismo
  • Inmutabilidad

@HenarMendiola

¿Qué es PF?    


Imperativa

¿Cómo?

Declarativa

¿Qué?

@HenarMendiola

Imperativa

¿Cómo?

@HenarMendiola

¿Qué es PF?    


Declarativa

¿Qué?

@HenarMendiola

¿Qué es PF?    


Funciones puras

  • Sin efectos secundarios.

 

  • Solo depende de sus argumentos

 

@HenarMendiola

¿Qué es PF?    


Funciones puras

let pi=3,1416

let getLength=radius=>{
    return 2*pi*radius;
}

let newPi=()=>{
    pi=acos(-1,0);
}
let getLength=radius=>{
    return 2*acos(-1,0)*radius;
}

Pura

Impura

@HenarMendiola

¿Qué es PF?    


Determinismo

let getLength=radius=>{
    return 2*acos(-1,0)*radius;
}

const lengthOne = getLength(5) //lengthOne = 31,4159265;

const lengthTwo = getLength(5) //lengthTwo  = 31,4159265;

const lengthThree = getLength(5) //lengthThree = 31,4159265;

const lengthFour = getLength(5) //lengthFour = 31,4159265;

@HenarMendiola

¿Qué es PF?    


Inmutabilidad

const array = [3, 6, 4, 5, 8]

array.push(9)

console.log(array) //[3, 6, 4, 5, 8, 9]

@HenarMendiola

¿Qué es PF?    


Inmutabilidad

const array = [3, 6, 4, 5, 8];

const newArray = array.concat(9);

console.log(array); //[3, 6, 4, 5, 8]

console.log(newArray); //[3, 6, 4, 5, 8, 9]

@HenarMendiola

¿Qué es PF?    


Una aplicación puramente funcional...

... o casi.

@HenarMendiola

Aplicación  

 

@HenarMendiola

Aplicación  

 

@HenarMendiola

Librerías

 

@HenarMendiola

La aplicación

data

[{title, img, description}]

planet

server

title

description

img

DOM

@HenarMendiola

La parte no funcional


import {
  getDOMElement,
  getData,
  processRes,
} from './src/intentions.js';


//impure functions
const addListenerToElement = (element, f) => {
  element.addEventListener("click", () => f(document));
}


const buttonListenerFunction = (dom) => {
  const res = R.compose(getData, getDOMElement)(dom, '#planet', 'value');
  res().then(res => res.json().then(data => processRes(data)));
}

@HenarMendiola

La aplicación

data

[{title, img, description}]

planet

server

title

description

img

DOM

Funciones elementales

  • Coger el valor del input
const getDOMelement = (selector, prop) => prop ? 
	document.querySelector(selector)[prop] : document.querySelector(selector);
  • Hacer la petición de datos
const getData = planet  => fetch(url+planet);
  • Procesar los datos
const getCollection = R.prop('collection');
const getItems = R.prop('items');

@HenarMendiola

Intenciones Simples

 

Funciones elementales

  • Procesar los datos
const processItem = item => ({ 
	title: item.data[0].title, 
    	img: getImageFromItem(item.links).getValue(), 
    	description: item.data[0].description
 });
  • Generar las tarjetas
const generateCardTemplate = (styles) => (info) => {
  return html`<div style=${styleMap(styles.card)}>
    <h2>${info.title}</h2>
    <div style=${styleMap(styles.info)}>
      <img style=${styleMap(styles.img)} src=${info.img}>
      <p>${info.description}</p>
    </div>
  </div>`
}

@HenarMendiola

Intenciones Simples

 

@HenarMendiola

La aplicación

data

[{title, img, description}]

planet

server

title

description

img

DOM

Funciones compuestas

data

collection

items

@HenarMendiola

Intenciones Compuestas

 

getCollection

getItems

Funciones compuestas

data

items

@HenarMendiola

Intenciones Compuestas

 

Funciones compuestas

data

items

@HenarMendiola

Intenciones Compuestas

 

compose(getItems,getCollection)

Funciones compuestas

  • Procesar la información
const getHrefFromElement0 = R.compose(R.prop('href'), R.prop('0'));

@HenarMendiola

Intenciones Compuestas

 

item = [
  { 
    href: "imagesource", 
    ...
  }, 
    ...
  ]
 "imagesource",
{ 
    href: "imagesource", 
    ...
  }

R.prop('0')

R.prop('href')

Funciones compuestas

  • Procesar la información
const getHrefFromElement0 = R.compose(R.prop('href'), R.prop('0'));

@HenarMendiola

Intenciones Compuestas

 

item = [
  { 
    href: "imagesource", 
    ...
  }, 
    ...
  ]
 "imagesource",

Funciones compuestas

  • Procesar la información
const getHrefFromElement0 = R.compose(R.prop('href'), R.prop('0'));

@HenarMendiola

Intenciones Compuestas

 

item = [
  { 
    href: "imagesource", 
    ...
  }, 
    ...
  ]
 "imagesource",

getHrefFromElement0

Funciones compuestas

  • Procesar la información
const processInfo = R.compose(R.map(processItem), getItems, getCollection );

@HenarMendiola

Intenciones Compuestas

 

const res = {
  collection: {
    items: [
      {...},
      {...},
      {...}
    ]
  }
}
{
  items: [
    {...},
    {...},
    {...}
  ]
}
[
    {...},
    {...},
    {...}
 ];
[
    {title, img, description},
    {title, img, description},
    {title, img, description}
 ];

getCollection

getItems

R.map(processItem)

Funciones compuestas

  • Procesar la información
const processInfo = R.compose(R.map(processItem), getItems, getCollection );

@HenarMendiola

Intenciones Compuestas

 

const res = {
  collection: {
    items: [
      {...},
      {...},
      {...}
    ]
  }
}
[
    {title, img, description},
    {title, img, description},
    {title, img, description}
 ];

processInfo

Funciones compuestas

LA FUNCIÓN

@HenarMendiola

Intenciones Compuestas

 

Funciones compuestas

  • LA FUNCIÓN
const processRes = R.compose(
	renderToSearchContainer, 
    	R.map(generateCardTemplate(styles)),
    	processInfo
  );

@HenarMendiola

Intenciones Compuestas

 

respuesta

información procesada

card-templates

card-templates

DOM

processInfo

generateCardTemplate

renderToSearchContainer

Funciones compuestas

  • LA FUNCIÓN
const processRes = R.compose(
	renderToSearchContainer, 
    	R.map(generateCardTemplate(styles)),
    	processInfo
  );

@HenarMendiola

Intenciones Compuestas

 

respuesta

card-templates

DOM

processRes

Ventajas y aplicaciones

  • Evaluación perezosa.
  • Fácil de leer.
  • Fácil de testear.

@HenarMendiola

Intenciones Compuestas

 

@HenarMendiola

Contextos seguros

 

class Maybe {
    constructor(value){
        this._value = value;
    }
    ...
  };

@HenarMendiola

Contextos seguros

 

class Maybe {
 	...
    static Just(value){
        return new Maybe(value);
    }
    static Nothing(){
        return new Maybe();
    }
  };

@HenarMendiola

Contextos seguros

 

class Maybe {
 	...
    map(f){      
      return this.isNothing() ? Maybe.Nothing() : Maybe.Just(f(this._value));
    }
    getValue(){
      return this.isNothing() ? './icons/txomski.jpg' : this._value;
    }
  };

@HenarMendiola

Contextos seguros

 

Ventajas y aplicaciones

 

  • Reutilización de funciones para contextos que implementen el método map.
  • Prevención ruptura flujo aplicativo.

@HenarMendiola

Contextos seguros

 

@HenarMendiola

Efectos secundarios

 

const a = 7;

function changeA(){
	a = 8;
}

@HenarMendiola

Efectos secundarios

 

¿Qué son los efectos secundarios?

DOM

CONSOLA

EL MUNDO EXTERIOR

@HenarMendiola

Efectos secundarios

 

Inyección de dependencias

const getDOMElement = 
	(dom, selector, prop) => 
    	prop ? dom.querySelector(selector)[prop] : dom.querySelector(selector);

@HenarMendiola

Efectos secundarios

 

const getDOMElement = 
	(selector, prop) => 
    	prop ? document.querySelector(selector)[prop] : document.querySelector(selector);

Antes

Después

Inyección de dependencias

const fakeDOM = (nodes) => ({
    nodes,
    querySelector: (node) => nodes[node],
})
it('getDOMElement should return the element from the DOM if it exits', () => {
  const DOM = fakeDOM({ input: { value: 'pluto' }});
  const inputValue = getDOMElement(DOM, 'input', 'value');
  assert.equal(inputValue, 'pluto')
});

@HenarMendiola

Efectos secundarios

 

Evaluación perezosa

const getData = planet  => () => fetch(url+planet);

@HenarMendiola

Efectos secundarios

 

Antes

Después

const getData = planet => fetch(url+planet);

Evaluación perezosa

    it('getData returns a function with no name and no arguments', () =>{
      const planet = 'earth';
      const fetchFunc = getData(planet);
      assert.equal(typeof fetchFunc, 'function');
      assert.equal(fetchFunc.name, '');
      assert.equal(fetchFunc.length, 0);
    });

@HenarMendiola

Efectos secundarios

 

Ventajas y aplicaciones

  • Control del flujo que implique efectos secundarios.
  • Más fácil de testear.

@HenarMendiola

Efectos secundarios

 

Currificación

Aplicación parcial

@HenarMendiola

Reutilización

 

Currificación

const addThreeNumbers = (a, b, c) => a + b + c;

const curryfiedAddThree = (a) => (b, c) => a + b + c;
const curryfiedAddThree = (a, b) => (c) => a + b + c;
const curryfiedAddThree = (a, b, c) => a + b + c;

@HenarMendiola

Reutilización

 

Currificación

export const generateCardTemplate = (styles, info) => {
  return html`<div style=${styleMap(styles.card)}>
    <h2>${info.title}</h2>
    <div style=${styleMap(styles.info)}>
      <img style=${styleMap(styles.img)} src=${info.img}>
      <p>${info.description}</p>
    </div>
  </div>`
}

@HenarMendiola

Reutilización

 

Currificación

export const generateCardTemplate = (styles) => (info) => {
  return html`<div style=${styleMap(styles.card)}>
    <h2>${info.title}</h2>
    <div style=${styleMap(styles.info)}>
      <img style=${styleMap(styles.img)} src=${info.img}>
      <p>${info.description}</p>
    </div>
  </div>`
}

@HenarMendiola

Reutilización

 

Aplicación parcial

const add = (a, b) => a + b;

const addOne = (a) => add(a, 1);
const addTwo = (a) => add(a, 2);

console.log(addOne(3)) // 4;
console.log(addOne(5)) // 7;

@HenarMendiola

Reutilización

 

Aplicación parcial

const getCollection = R.prop('collection');
const getItems = R.prop('items');

Antes

Después

const collection = R.prop('collection', data);
const items = R.prop('items', collection);

@HenarMendiola

Reutilización

 

Ventajas y aplicaciones

  • Composición de funciones.
  • Evaluación perezosa.
  • Reutilización.

@HenarMendiola

Reutilización

 

Resumiendo...

  • Es posible utilizar PF en tu día a día.
  • Contextos seguros de trabajo.
  • Herramientas de reutilización de código.
  • Mayor control sobre los efectos secundarios.
  • Fácil de leer.
  • Fácil de testear.
  • Fácil de mantener.

@HenarMendiola

Conclusiones

 

@HenarMendiola

Bibliografía

 

@HenarMendiola

Gracias y preguntas