chaining et composition de fonctions avec lodash / underscore

38
CHAINING CHAINING ET ET COMPOSITION COMPOSITION DE DE FONCTIONS AVEC _ FONCTIONS AVEC _

Upload: nicolas-carlo

Post on 13-Jan-2017

177 views

Category:

Internet


2 download

TRANSCRIPT

Page 1: Chaining et composition de fonctions avec lodash / underscore

CHAININGCHAINING ET ET COMPOSITIONCOMPOSITION DE DEFONCTIONS AVEC _FONCTIONS AVEC _

Page 3: Chaining et composition de fonctions avec lodash / underscore

AVANT DE AVANT DE PLONGERPLONGER> PETIT CHECK-UP> PETIT CHECK-UP

Page 4: Chaining et composition de fonctions avec lodash / underscore

QUAND JE DIS QUAND JE DIS __Personnellement j'utilise (v3.0+)Ça marche aussi avec (pour l'essentiel) \o/

lodashunderscore

Page 5: Chaining et composition de fonctions avec lodash / underscore

FUNCTIONAL PROGRAMMING?FUNCTIONAL PROGRAMMING?First-class functionsHigher order functionsPure functionsRecursionImmutability≠ imperative programmingmais !≠ OOP

Page 6: Chaining et composition de fonctions avec lodash / underscore

CHAININGCHAINING DE FONCTIONS DE FONCTIONS> ET POURQUOI FAIRE ?> ET POURQUOI FAIRE ?

Page 7: Chaining et composition de fonctions avec lodash / underscore

CAS CONCRET : OPÉRATIONS MULTIPLES (0)CAS CONCRET : OPÉRATIONS MULTIPLES (0)

this.set( "items", [ { id: "ae213aa4", cepage: "merlot", type: "juice", quantity: 3 }, null, { id: "ebaaa82e", cepage: "gamay", type: "grape", quantity: 2 }, { id: "ee2bcc12", cepage: "viognier", type: "grape", quantity: 0 }] );

Page 8: Chaining et composition de fonctions avec lodash / underscore

CAS CONCRET : OPÉRATIONS MULTIPLES (1)CAS CONCRET : OPÉRATIONS MULTIPLES (1)

function getItems() { return _.compact( this.get( "items" ) );}

getItems();// => // [ // { id: "ae213aa4", cepage: "merlot", type: "juice", quantity: 3 },// { id: "ebaaa82e", cepage: "gamay", type: "grape", quantity: 2 },// { id: "ee2bcc12", cepage: "viognier", type: "grape", quantity: 0 }// ]

Page 9: Chaining et composition de fonctions avec lodash / underscore

CAS CONCRET : OPÉRATIONS MULTIPLES (2)CAS CONCRET : OPÉRATIONS MULTIPLES (2)

function getItems() { return _.reject( _.compact( this.get( "items" ) ), { quantity: 0 } );}

getItems();// => // [ // { id: "ae213aa4", cepage: "merlot", type: "juice", quantity: 3 },// { id: "ebaaa82e", cepage: "gamay", type: "grape", quantity: 2 }// ]

Page 10: Chaining et composition de fonctions avec lodash / underscore

CAS CONCRET : OPÉRATIONS MULTIPLES (3)CAS CONCRET : OPÉRATIONS MULTIPLES (3)function getItems() { return _.filter( _.reject( _.compact( this.get( "items" ) ), { quantity: 0 } ), { type: "juice" } );}

getItems();// => // [ // { id: "ae213aa4", cepage: "merlot", type: "juice", quantity: 3 }// ]

Page 11: Chaining et composition de fonctions avec lodash / underscore

CAS CONCRET : OPÉRATIONS MULTIPLES (3)CAS CONCRET : OPÉRATIONS MULTIPLES (3)// Better, really?function getItems() { var compactedItems = _.compact( this.get( "items" ) ); var positiveCompactedItems = _.reject( compactedItems, { quantity: 0 } );

return _.filter( positiveCompactedItems, { type: "juice" } );}

getItems();// => // [ // { id: "ae213aa4", cepage: "merlot", type: "juice", quantity: 3 }// ]

Page 12: Chaining et composition de fonctions avec lodash / underscore

_.CHAIN(_.CHAIN( VALUE VALUE ))Creates a lodash object that wraps value with explicitmethod chaining enabled.

_.chain( this.get( "items" ) );// => returns `LodashWrapper`

_.chain( this.get( "items" ) ).compact();// <=> `_.compact( this.get( "items" ) );`// BUT… returns `LodashWrapper` too!

// And so we can do ->_.chain( this.get( "items" ) ) .compact() .reject( { quantity: 0 } ) .filter( { type: "juice" } ) // … .map( doSomething );

Page 13: Chaining et composition de fonctions avec lodash / underscore

_.CHAIN(_.CHAIN( VALUE VALUE )) … … .VALUE().VALUE() ! !Executes the chained sequence to extract theunwrapped value.

_.chain( this.get( "items" ) ) .compact() .reject( { quantity: 0 } ) .filter( { type: "juice" } ) // Hum… still return `LodashWrapper` >_<

.value(); // And voilà \o/

// => // [ // { id: "ae213aa4", cepage: "merlot", type: "juice", quantity: 3 }// ]

Page 14: Chaining et composition de fonctions avec lodash / underscore

CAS CONCRET : OPÉRATIONS MULTIPLES (4)CAS CONCRET : OPÉRATIONS MULTIPLES (4)function getItems() { return _.chain( this.get( "items" ) ) .compact() .reject( { quantity: 0 } ) .filter( { type: "juice" } ) .value();}

getItems();// => // [ // { id: "ae213aa4", cepage: "merlot", type: "juice", quantity: 3 }// ]

function getItems() { return _( this.get( "items" ) ) // _( value ) === _.chain( value ) .compact() .reject( { quantity: 0 } ) .filter( { type: "juice" } ) .value();}

getItems();// => // [ // { id: "ae213aa4", cepage: "merlot", type: "juice", quantity: 3 }// ]

Page 15: Chaining et composition de fonctions avec lodash / underscore

TOUT ÇA POUR TOUT ÇA POUR ÇAÇA ? ?> INTÉRÊTS DU CHAINING> INTÉRÊTS DU CHAINING

Page 16: Chaining et composition de fonctions avec lodash / underscore

PIPELINE / FLOWPIPELINE / FLOWfunction getItems() { return _( this.get( "items" ) ) .compact() .reject( isEmpty ) .filter( isJuice ) .map( parseText ) // … we construct the pipeline // flow is clear, readable! .value();}

Ça vous rappelle quelque chose ?

Page 17: Chaining et composition de fonctions avec lodash / underscore

PIPELINE / FLOWPIPELINE / FLOWfunction makeItemAvailable( userID, index ) { return _findOneItem( userID, index ) .then( doSomethingClever ) .then( updateStatusAs( "available" ) ) .then( res.ok ) .catch( res.serverError );}

// You get the same idea with promises.

Page 18: Chaining et composition de fonctions avec lodash / underscore

function getBottles( options ) { // Ensure default options. options = _.defaults( {}, options, { isAppellationOnly: false } );

var bottlesWrapper = _( this.get( "bottles" ) ).map( parseText );

// …

// Dynamically build the pipeline. if( options.isAppellationOnly ) { bottlesWrapper = bottlesWrapper.pick( [ "appellation" ] ); }

// Nothing have been computed so far! return bottlesWrapper.value(); // evaluates when needed only!}

function getParsedBottlesWrapper() { return _( this.get( "bottles" ) ).map( parseText );}

function getBottles( options ) { // Ensure default options. options = _.defaults( {}, options, { isAppellationOnly: false } );

var bottlesWrapper = getParsedBottlesWrapper.call( this );

// Dynamically build the pipeline. if( options.isAppellationOnly ) { bottlesWrapper = bottlesWrapper.pick( [ "appellation" ] ); }

// Nothing have been computed so far! return bottlesWrapper.value(); // evaluates when needed only!}

LAZYLAZY EVALUATION EVALUATION

Page 19: Chaining et composition de fonctions avec lodash / underscore

LAZYLAZY EVALUATION EVALUATIONPour en savoir plus >

http://filimanjaro.com/blog/2014/introducing-lazy-evaluation/

Page 20: Chaining et composition de fonctions avec lodash / underscore

COMPOSITIONCOMPOSITION ET AUTRES RUSES ET AUTRES RUSES> POUR CONSTRUIRE DES PIPELINES > POUR CONSTRUIRE DES PIPELINES EFFICACESEFFICACES

Page 21: Chaining et composition de fonctions avec lodash / underscore

COMPOSITION ?COMPOSITION ?(f ⋅ g)(x) = f(g(x))

function add10( value ) { // f return 10 + value;}

function times3( value ) { // g return 3 * value;}

add10( times3( 10 ) ); // (f ∘ g)( 10 )// => 10 + ( 3 * 10 )// => 40

Page 22: Chaining et composition de fonctions avec lodash / underscore

COMPOSITION ?COMPOSITION ?

function add10( value ) { // f return 10 + value;}

function times3( value ) { // g return 3 * value;}

var times3AndAdd10 = _.compose( add10, times3 ); // f ∘ g

times3AndAdd10( 10 );// => 40

times3AndAdd10( 0 );// => 10

function add10( value ) { // f return 10 + value;}

function times3( value ) { // g return 3 * value;}

var times3AndAdd10 = _.flowRight( add10, times3 ); // f ∘ g

times3AndAdd10( 10 );// => 40

times3AndAdd10( 0 );// => 10

Page 23: Chaining et composition de fonctions avec lodash / underscore

_.FLOWRIGHT(_.FLOWRIGHT( [FUNCS] [FUNCS] ))Crée une fonction qui retourne le résultat des funcs oùchacune est invoquée avec le résultat de la fonction qui laprécède, de la droite vers la gauche (= compose).

Page 24: Chaining et composition de fonctions avec lodash / underscore

_.FLOW(_.FLOW( [FUNCS] [FUNCS] ))

function add10( value ) { // f return 10 + value;}

function times3( value ) { // g return 3 * value;}

var times3AndAdd10 = _.flow( times3, add10 ); // f ∘ g

times3AndAdd10( 10 );// => 40

times3AndAdd10( 0 );// => 10

Si _.flowRight n'est pas intuitif pour vous.

Page 25: Chaining et composition de fonctions avec lodash / underscore

APPLICATION PARTIELLE : APPLICATION PARTIELLE : _.PARTIAL()_.PARTIAL()

function greet( greeting, name ) { return greeting + " " + name;}

var sayHelloTo = _.partial( greet, "Hello" );// returns a function with params partially set.

sayHelloTo( "Backbone" );// → "Hello Backbone"

Page 26: Chaining et composition de fonctions avec lodash / underscore

APPLICATION PARTIELLEAPPLICATION PARTIELLE

function _isCepageInRecipe( cepage, bottle ) { … }function _areBuildingsPartOfRecipe( buildings, bottle ) { … }

function hasMatchingBottles( cepage, buildings ) { var isCepageInRecipe = _.partial( _isCepageInRecipe, cepage ); var areBuildingsPartOfRecipe = _.partial( _areBuildingsPartOfRecipe, buildings );

return _( this.get( "bottles" ) ) .filter( isCepageInRecipe ) .any( areBuildingsPartOfRecipe );}

Pour pouvoir chaîner dans la vraie vie…

Page 27: Chaining et composition de fonctions avec lodash / underscore

function greet( greeting, name ) { return greeting + " " + name;}

var greetBackbone = _.partialRight( greet, "Backbone" );// returns a function with params partially set.

greetBackbone( "Hello" );// → "Hello Backbone"

_.PARTIALRIGHT()_.PARTIALRIGHT()

Page 28: Chaining et composition de fonctions avec lodash / underscore

// Not so smart params order here…function _isCepageInRecipe( bottle, cepage ) { … }function _areBuildingsPartOfRecipe( bottle, buildings ) { … }

// Not so smart params order here…function _isCepageInRecipe( bottle, cepage ) { … }function _areBuildingsPartOfRecipe( bottle, buildings ) { … }

function hasMatchingBottles( cepage, buildings ) { // Use `_` as a placeholder for not-yet-defined params! var isCepageInRecipe = _.partial( _isCepageInRecipe, _, cepage ); var areBuildingsPartOfRecipe = _.partial( _areBuildingsPartOfRecipe, _, buildings );

return _( this.get( "bottles" ) ) .filter( isCepageInRecipe ) .any( areBuildingsPartOfRecipe );}

// Not so smart params order here…function _isCepageInRecipe( bottle, cepage ) { … }function _areBuildingsPartOfRecipe( bottle, buildings ) { … }

function hasMatchingBottles( cepage, buildings ) { // Use `_` as a placeholder for not-yet-defined params! var isCepageInRecipe = _.partialRight( _isCepageInRecipe, cepage ); var areBuildingsPartOfRecipe = _.partialRight( _areBuildingsPartOfRecipe, buildings );

return _( this.get( "bottles" ) ) .filter( isCepageInRecipe ) .any( areBuildingsPartOfRecipe );}

APPLICATION PARTIELLE : LE COUTEAU SUISSEAPPLICATION PARTIELLE : LE COUTEAU SUISSE

Page 29: Chaining et composition de fonctions avec lodash / underscore

COMPOSITION COMPOSITION VS.VS. CHAINING ? CHAINING ?_.flow est un outil pour créer des higher order functionsPeut éventuellement remplacer des chaînes simples…… mais pas toujours adapté pour remplacer _.chain

function getJuices( items ) { return _( items ) .compact() .reject( { quantity: 0 } ) .filter( { type: "juice" } ) .value();}

// Flow equivalentvar getJuices = _.flow( _.partialRight( _.filter, { type: "juice" } ), _.partialRight( _.reject, { quantity: 0 } ), _.compact);

Page 30: Chaining et composition de fonctions avec lodash / underscore

BUT WAIT, THERE'S BUT WAIT, THERE'S MOREMORE> QUELQUES > QUELQUES SUBTILITÉSSUBTILITÉS ET GRANDS CLASSIQUES ET GRANDS CLASSIQUES

Page 31: Chaining et composition de fonctions avec lodash / underscore

TOUT N'EST TOUT N'EST PASPAS CHAINABLE CHAINABLEIl y a des méthodes qui le sont : _.keys, _.map, _.push,_.pluck, _.union, … D'autres qui ne le sont pas (par défaut) : _.find,_.isNumber, _.reduce, _.sum, …

Page 32: Chaining et composition de fonctions avec lodash / underscore

MÉTHODES NON-CHAINABLESMÉTHODES NON-CHAINABLESfunction getJuiceTotalQuantity() { return _( this.get( "items" ) ) .compact() .filter( isJuice ) .pluck( "quantity" ) .sum(); // => return the sum // no need for `.value()` -> implicitly called}

Plus d'infos sur la doc > https://lodash.com/docs#_

Page 33: Chaining et composition de fonctions avec lodash / underscore

_.PROTOTYPE.PLANT(_.PROTOTYPE.PLANT( VALUE VALUE ))Crée un clone de la chaîne avec la value donnée

var wrapper = _( [ 1, 2, null, 3 ] ).compact();

var otherWrapper = wrapper.plant( [ "a", null, "b", undefined ] );

wrapper.value();// => [ 1, 2, 3 ]

otherWrapper.value();// => [ "a", "b" ]

Page 34: Chaining et composition de fonctions avec lodash / underscore

_.PROTOTYPE.COMMIT()_.PROTOTYPE.COMMIT()Exécute la chaîne et retourne un wrapper.

var array = [ 1, 2, 3 ];var wrapper = _( array ).push( 2 );

console.log( array );// => [ 1, 2, 3 ]// Nothing executed, nothing changed.

wrapper = wrapper.commit();

console.log( array );// => [ 1, 2, 3, 2 ]// Chain executed// `_.push()` mutated the original `array`

wrapper.without( 2 ).value();// => [ 1, 3 ]

Page 35: Chaining et composition de fonctions avec lodash / underscore

_.TAP(_.TAP( VALUE, INTERCEPTOR, [THISARG] VALUE, INTERCEPTOR, [THISARG] ))Invoque interceptor et retourne value.

_( [ 1, 2, null, 3 ] ) .compact() .tap( function ( value ) { console.log( "tapped ->", value ); } ) .push( 1 ) .value();

// => "tapped -> [ 1, 2, 3 ]"// => "[ 1, 2, 3, 1 ]"

"Tap" dans la chaîne = très utile pour debug !

Page 36: Chaining et composition de fonctions avec lodash / underscore

_.TAP(_.TAP( VALUE, INTERCEPTOR, [THISARG] VALUE, INTERCEPTOR, [THISARG] ))Pour log une valeur intermédiaire

_( [ 1, 2, null, 3 ] ) .compact() // Can use `console.log`, just don't forget to bind the context! .tap( console.log.bind( console ) ) .push( 1 ) .value();

// => "[ 1, 2, 3 ]"// => "[ 1, 2, 3, 1 ]"

Page 37: Chaining et composition de fonctions avec lodash / underscore

_.THRU(_.THRU( VALUE, INTERCEPTOR, [THISARG] VALUE, INTERCEPTOR, [THISARG] ))Invoque interceptor et retourne la value de l'interceptor.

_( [ 1, 2, null, 3 ] ) .compact() .thru( function ( value ) { console.log( "tapped ->", value );

// Don't forget to return a value for the chain. return value.reverse(); } ) .push( 0 ) .value();

// => "tapped -> [ 1, 2, 3 ]"// => "[ 3, 2, 1, 0 ]"

Page 38: Chaining et composition de fonctions avec lodash / underscore

MERCI !MERCI ! DES QUESTIONS ? DES QUESTIONS ?