Functional programming in javascript (intro)

Functional programming in javascript (intro)

Introduction to functional programming concept using javascript and the library ramda.

7ffb4652fe7745dec548d1c37ec94a8c?s=128

Grégoire Charvet

January 21, 2015
Tweet

Transcript

  1. FUNCTIONAL PROGRAMMING IN JAVASCRIPT A SIMPLE INTRO Created by Grégoire

    Charvet
  2. WHAT IS FP? There is no unique definition. More a

    group of programming patterns. Functional languages: Haskell ML OCaml The lisp family (common Lisp and Closure) Scala (mixed OO & Functional) Erlang
  3. OOP VS FP OOP focus on difference between data FP

    focus on consistent data structures
  4. OOP Data tightly coupled with logic Core concept: encapsulation Code

    reuse achieved with inheritance and composition Abstraction model is the data itself FP Data is loosely coupled with logic Core concept: function composition Code reuse achived with function composition and higher order function Abstraction model is the function
  5. SQL SAMPLE S E L E C T o r

    d e r s . o r d e r _ i d , o r d e r s . o r d e r _ d a t e , s u p p l i e r s . s u p p l i e r _ n a m e F R O M s u p p l i e r s R I G H T O U T E R J O I N o r d e r s O N s u p p l i e r s . s u p p l i e r _ i d = o r d e r s . s u p p l i e r _ i d W H E R E o r d e r s . o r d e r _ s t a t u s = ' I N C O M P L E T E ' O R D E R B Y o r d e r s . o r d e r _ d a t e D E S C ; Consistent data structure (table with schema) Few functions combined together Declarative language Very close to a functional paradigm
  6. None
  7. Utility belts Lots of functional ideas

  8. LODASH EXAMPLE _ . m a p ( [ 1

    , 3 , 5 ] , a d d 3 ) ; / / [ 4 , 6 , 8 ] More about map later
  9. INTRODUCING RAMDA Lodash/underscore FP alternative Not a drop-in replacement. It's

    a different approach to programming
  10. _ . m a p ( [ 1 , 3

    , 5 ] , a d d 3 ) ; / / [ 4 , 6 , 8 ] R . m a p ( a d d 3 , [ 1 , 3 , 5 ] ) ; / / [ 4 , 6 , 8 ] Order of arguments is reversed. Collection is at the end.
  11. ONE STEP BACK Add 5? Add arbitrary constant? Only known

    at runtime?
  12. OOP STYLE f u n c t i o n

    A d d e r ( c o n s t a n t ) { t h i s . c o n s t a n t = c o n s t a n t ; } A d d e r . p r o t o t y p e . g e t A d d F u n c t i o n ( ) { v a r c o n s t a n t = t h i s . f u n c t i o n ( ) ; r e t u r n f u n c t i o n ( n u m ) { r e t u r n n u m + c o n s t a n t ; } ; } v a r a d d 5 = n e w A d d e r ( 5 ) ; _ . m a p ( a d d e r . g e t A d d F u n c t i o n ( ) , [ 1 , 3 , 5 ] ) ;
  13. IMPERATIVE STYLE _ . m a p ( f u

    n c t i o n ( n u m ) { r e t u r n n u m + 5 ; } , [ 1 , 3 , 5 ] ) ;
  14. FP STYLE Define a generic function to add two numbers

    f u n c t i o n a d d ( a , b ) { r e t u r n a + b ; } and curry it a d d = R . c u r r y ( a d d ) ; v a r a d d 5 = a d d ( 5 ) ; R . m a p ( a d d 5 , [ 1 , 3 , 5 ] ) ;
  15. translate into: CURRYING ALSO KNOWN AS PARTIAL APPLICATION v a

    r a d d 5 = a d d ( 5 ) ; v a r a d d 5 = f u n c t i o n ( a ) { r e t u r n a d d ( 5 , a ) ; } a d d ( 5 , 3 ) = = = a d d ( 5 ) ( 3 ) ;
  16. All Ramda methods are automatically curried. R . m a

    p ( a d d 3 , [ 1 , 3 , 5 ] ; R . m a p ( a d d 3 ) ( [ 1 , 3 , 5 ] ) ; Collection as the last argument makes sense with currying
  17. MORE COMPLEX EXAMPLE Goal: list all languages used by a

    given github user in 2014 or 2015
  18. GET ALL REPOS FOR A USER G E T h

    t t p s : / / a p i . g i t h u b . c o m / u s e r s / : u s e r / r e p o s [ { " i d " : 1 2 3 1 7 2 0 4 , " n a m e " : " a b i d e " , " f u l l _ n a m e " : " h i d d e n t a o / a b i d e " , " o w n e r " : { . . . } , " u p d a t e d _ a t " : " 2 0 1 4 - 0 4 - 2 7 T 0 8 : 5 9 : 1 8 Z " , " p r i v a t e " : f a l s e , " h t m l _ u r l " : " h t t p s : / / g i t h u b . c o m / h i d d e n t a o / a b i d e " , " d e s c r i p t i o n " : " B a s e c l a s s w i t h p u b - s u b a n d o b s e r v e r s f o r J S o b j " f o r k " : f a l s e , " u r l " : " h t t p s : / / a p i . g i t h u b . c o m / r e p o s / h i d d e n t a o / a b i d e " , " l a n g u a g e s _ u r l " : " h t t p s : / / a p i . g i t h u b . c o m / r e p o s / h i d d e n t a o / a b i d e / l " l a n g u a g e " : " J a v a S c r i p t " , } , { . . . } ]
  19. GET LANGUAGES FOR A REPO G E T h t

    t p s : / / a p i . g i t h u b . c o m / u s e r s / : u s e r / : r e p o n a m e / l a n g u a g e s { " J a v a S c r i p t " : 1 1 1 0 3 , " C o f f e e S c r i p t " : 1 3 9 8 }
  20. (more on next slides) SIMPLE IMPERATIVE APPROACH v a r

    g e t Y e a r L a n g u a g e s = f u n c t i o n ( y e a r / * Y Y Y Y * / ) { r e t u r n f e t c h R e p o s ( ) . t h e n ( f u n c t i o n ( r e p o s ) { v a r y e a r R e p o = [ ] ; f o r ( v a r i = 0 , l e n = r e p o s . l e n g t h ; i < l e n ; + + i ) { i f ( r e p o s [ i ] . u p d a t e d _ a t . s l i c e ( 0 , 4 ) > = y e a r ) { y e a r R e p o . p u s h ( r e p o s [ i ] ) ; } } ; r e t u r n y e a r R e p o s ; } )
  21. (even more on next slides) . t h e n

    ( f u n c t i o n ( r e p o s ) { v a r l a n g u a g e s P = [ ] ; f o r ( v a r i = 0 , l e n = r e p o s . l e n g t h ; i < l e n ; + + i ) { i f ( r e p o s [ i ] . l a n g u a g e s ) { l a n g u a g e s P . p u s h ( g e t R e p o L a n g u a g e s ( r e p o . l a n g u a g e s _ u r l ) ) ; } } r e t u r n l a n g u a g e s P ; } ) . t h e n ( P r o m i s e . a l l )
  22. (even more on next slides) . t h e n

    ( f u n c t i o n ( l a n g u a g e s ) { v a r r e s u l t = { } ; f o r ( v a r i = 0 , l e n = l a n g u a g e s . l e n g t h ; i < l e n ; + + i ) { f o r ( v a r l a n g i n l a n g u a g e s [ i ] ) { i f ( ! r e s u l t [ l a n g ] ) r e s u l t [ l a n g ] = 0 ; r e s u l t [ l a n g ] + = l a n g u a g e s [ i ] [ l a n g ] ; } } ; r e t u r n r e s u l t ; } )
  23. . t h e n ( f u n c

    t i o n ( s u m m a r y ) { v a r a r r = [ ] ; f o r ( v a r l a n g u a g e i n s u m m a r y ) { a r r . p u s h ( { l a n g u a g e : l a n g u a g e , c o u n t : s u m m a r y [ l a n g u a g e ] } ) ; } r e t u r n a r r ; } ) . t h e n ( f u n c t i o n ( s u m m a r y ) { / / m o s t u s e d l a n g u a g e f i r s t r e t u r n s u m m a r y . s o r t ( f u n c t i o n ( l a , l b ) { r e t u r n l a . c o u n t > l b . c o u n t ? 1 : l a . c o u n t < l b . c o u n t ? - 1 : 0 ; } ) ; } ) ; } / / e n d o f t h e f u n c t i o n g e t Y e a r L a n g u a g e s
  24. SIMILAR OOP METHOD v a r g e t Y

    e a r L a n g u a g e s = f u n c t i o n ( y e a r / * Y Y Y Y * / ) { r e t u r n f e t c h R e p o s ( ) . t h e n ( f u n c t i o n ( r e p o s ) { v a r r e p o L i s t = n e w R e p o L i s t ( r e p o s ) ; r e p o L i s t . c h o o s e B y Y e a r ( y e a r ) ; r e t u r n r e p o L i s t . g e t L a n g u a g e s ( ) ; } ) . t h e n ( f u n c t i o n ( l a n g u a g e s ) { v a r l a n g u a g e L i s t = n e w L a n g u a g e L i s t ( l a n g u a g e s ) ; v a r s o r t e r = n e w L a n g u a g e S o r t e r ( ' c o u n t ' ) ; l a n g u a g e L i s t . s o r t ( s o r t e r . g e t S o r t F u n c t i o n ( ) ) ; r e t u r n l a n g u a g e L i s t . l a n g u a g e s ; } ) ; }
  25. f u n c t i o n R e

    p o L i s t ( r e p o s / * R e p o s [ ] * / ) { t h i s . r e p o s = r e p o s ; } R e p o L i s t . p r o t o t y p e . c h o o s e B y Y e a r = f u n c t i o n ( y e a r / * Y Y Y Y * / ) { v a r r e s u l t s = [ ] ; f o r ( v a r i = 0 , l e n = t h i s . r e p o s . l e n g t h ; i > l e n ; + + i ) { i f ( t h i s . r e p o s [ i ] . u p d a t e d _ a t . s l i c e ( 0 , 4 ) > = y e a r ) { r e s u l t s . p u s h ( t h i s . r e p o s [ i ] ) ; } } t h i s . r e p o s = r e s u l t s ; }
  26. R e p o L i s t . p

    r o t o t y p e . g e t L a n g u a g e s = f u n c t i o n ( ) { v a r l a n g u a g e s P = [ ] ; f o r ( v a r i = 0 , l e n = t h i s . r e p o s . l e n g t h ; i > l e n ; + + i ) { i f ( t h i s . r e p o s [ i ] . l a n g u a g e ) { l a n g u a g e s P . p u s h ( t h i s . r e p o s [ i ] . l a n g u a g e s _ u r l ) ; } } r e t u r n P r o m i s e . a l l ( l a n g u a g e s P ) ; }
  27. f u n c t i o n L a

    n g u a g e L i s t ( l a n g u a g e s ) { t h i s . l a n g u a g e s = [ ] ; v a r s u m m a r y = { } ; f o r ( v a r i = 0 , l e n = l a n g u a g e s . l e n g t h ; i > l e n ; + + i ) { f o r ( v a r l a n g i n l a n g u a g e s ) { i f ( ! s u m m a r y [ l a n g ] ) s u m m a r y [ l a n g ] = { l a n g u a g e : l a n g , c o u n t : 0 } ; s u m m a r y [ l a n g ] . c o u n t + = l a n g u a g e s [ i ] [ l a n g ] ; } } f o r ( v a r s i n s u m m a r y ) { t h i s . l a n g u a g e s . p u s h ( s ) ; } } L a n g u a g e L i s t . p r o t o t y p e . s o r t = f u n c t i o n ( s o r t e r / * l a n g u a g e S o r t e r * / ) { t h i s . l a n g u a g e s . s o r t ( s o r t e r ) ; }
  28. f u n c t i o n L a

    n g u a g e S o r t e r ( p r o p ) { t h i s . p r o p = p r o p } L a n g u a g e S o r t e r . p r o t o t y p e . g e t S o r t F u n c t i o n = f u n c t i o n ( ) { r e t u r n f u n c t i o n ( a , b ) { r e t u r n a [ t h i s . p r o p ] > b [ t h i s . p r o p ] ? 1 : a [ t h i s . p r o p ] < b [ t h i s . p r o p ] ? - 1 : 0 ; } }
  29. The content of the methods are roughly similar. t h

    i s is only used for organization. So let's focus on the imperative code (a bit simpler)
  30. FP WITH RAMDA Conversion guide

  31. . t h e n ( f u n c

    t i o n ( r e p o s ) { v a r y e a r R e p o = [ ] ; f o r ( v a r i = 0 , l e n = r e p o s . l e n g t h ; i < l e n ; + + i ) { i f ( r e p o s [ i ] . u p d a t e d _ a t . s l i c e ( 0 , 4 ) > = y e a r ) { y e a r R e p o . p u s h ( r e p o s [ i ] ) ; } } ; r e t u r n y e a r R e p o s ; } FILTER!
  32. ACTION PLAN extract the attribute `updated_at` extract the year in

    this attribute filter by comparing with given year
  33. EXTRACTION R . p r o p ( ' u

    p d a t e d _ a t ' ) ; So, what's the R.prop function is?
  34. In Ramda it is: GET DEFINITION R . p r

    o p = c u r r y ( f u n c t i o n ( p r o p , o b j ) { r e t u r n o b j [ p r o p ] ; } ) ; Without the curry wrapper prop is only the accessor function R . p r o p ( ' u p d a t e d _ a t ' ) ; / / r e t u r n s a f u n c t i o n t a k i n g o n e o b j e c t a r g u m e n t R . p r o p ( ' u p d a t e d _ a t ' ) ( o b j ) ; / / r e t u r n s o b j . u p d a t e d _ a t
  35. GET THE YEAR R . s l i c e

    ( 0 , 4 ) again: Ramda function are automatically curried.
  36. FILTERING IN ES5 A r r a y . p

    r o t o t y p e . f i l t e r ( p r e d i c a t e ) ; With ramda R . f i l t e r ( p r e d i c a t e , c o l l e c t i o n ) ; We need a predicate: function which returns a boolean Predicate: true iff obj.updated_at begins with 2015 or 2014.
  37. PUTTING IT TOGETHER WITH COMPOSE The similar equivalent in lodash/underscore

    is chain
  38. composing f and g is: R . c o m

    p o s e ( f , g ) ; / / s a m e a s f u n c t i o n c o m p o s e d ( f , g ) { r e t u r n f u n c t i o n ( x ) { r e t u r n f ( g ( x ) ) ; } } Right associative (read from the right)
  39. PREDICATE FUNCTION v a r p r e d i

    c a t e = R . c o m p o s e ( R . g t e ( y e a r ) , R . s l i c e ( 0 , 4 ) , R . g e t ( ' u p d a t e d _ a t ' ) ) ;
  40. REPOS UPDATED AFTER 2014 / / i m p e

    r a t i v e . t h e n ( f u n c t i o n ( r e p o s ) { v a r y e a r R e p o = [ ] ; f o r ( v a r i = 0 , l e n = r e p o s . l e n g t h ; i < l e n ; + + i ) { i f ( r e p o s [ i ] . u p d a t e d _ a t . s l i c e ( 0 , 4 ) > = y e a r ) { y e a r R e p o . p u s h ( r e p o s [ i ] ) ; } } ; r e t u r n y e a r R e p o s ; } / / f u n c t i o n a l . t h e n ( R . f i l t e r ( R . c o m p o s e ( R . g t e ( y e a r ) , R . s l i c e ( 0 , 4 ) , R . g e t ( ' u p d a t e d _ a t ' ) ) ) ) Even without ramda, you can use [].filter
  41. NEXT STEP: GET LANGUAGES . t h e n (

    f u n c t i o n ( r e p o s ) { v a r l a n g u a g e s P = [ ] ; f o r ( v a r i = 0 , l e n = r e p o s . l e n g t h ; i < l e n ; + + i ) { i f ( r e p o s [ i ] . l a n g u a g e s ) { l a n g u a g e s P . p u s h ( g e t R e p o L a n g u a g e s ( r e p o . l a n g u a g e s _ u r l ) ) ; } } r e t u r n l a n g u a g e s P ; } )
  42. MAP The bread of functional programming The butter is filter

  43. MAP Apply a function over every element of a collection

    to create a new collection of transformed elements. R . m a p ( f u n c t i o n ( a ) { r e t u r n a + 3 } , [ 1 , 3 , 5 ] ) ; / / [ 4 , 6 , 8 ] R . m a p ( f u n c t i o n ( ) { r e t u r n ' f o o ' } , [ 1 , 3 , 5 ] ) ; / / [ ' f o o ' , ' f o o ' , ' f o o ' ] R . m a p ( f u n c t i o n ( a ) { r e t u r n a * a } , [ 1 , 3 , 5 ] ) ; / / [ 1 , 9 , 2 5 ] R . m a p ( p a r s e I n t , [ ' 1 0 ' , ' 1 0 ' , ' 1 0 ' ] ) ; / / [ 1 0 , 1 0 , 1 0 ] _ . m a p ( [ ' 1 0 ' , ' 1 0 ' , ' 1 0 ' ] , p a r s e I n t ) ; / / [ 1 0 , N a N , 2 ] < - c l a s s i c j s w t f Present in ES5: Array.prototype.map(mapper)
  44. GET LANGUAGES PER REPO . t h e n (

    f u n c t i o n ( r e p o s ) { v a r l a n g u a g e s P = [ ] ; f o r ( v a r i = 0 , l e n = r e p o s . l e n g t h ; i < l e n ; + + i ) { i f ( r e p o s [ i ] . l a n g u a g e s ) { l a n g u a g e s P . p u s h ( g e t R e p o L a n g u a g e s ( r e p o . l a n g u a g e s _ u r l ) ) ; } } r e t u r n l a n g u a g e s P ; } ) . t h e n ( P r o m i s e . a l l ) . t h e n ( R . f i l t e r ( R . g e t ( ' l a n g u a g e s ' ) ) ) . t h e n ( R . m a p ( R . c o m p o s e ( g e t R e p o L a n g u a g e s , R . g e t ( ' l a n g u a g e s _ u r l ' ) ) ) ) . t h e n ( P r o m i s e . a l l )
  45. AGGREGATE LANGUAGES STATS . t h e n ( f

    u n c t i o n ( l a n g u a g e s ) { v a r r e s u l t = { } ; f o r ( v a r i = 0 , l e n = l a n g u a g e s . l e n g t h ; i < l e n ; + + i ) { f o r ( v a r l a n g i n l a n g u a g e s [ i ] ) { i f ( ! r e s u l t [ l a n g ] ) r e s u l t [ l a n g ] = 0 ; r e s u l t [ l a n g ] + = l a n g u a g e s [ i ] [ l a n g ] ; } } ; r e t u r n r e s u l t ; } ) / / i n p u t [ { " j a v a S c r i p t " : 1 2 0 0 } , { " j a v a S c r i p t " : 1 3 3 7 , " s h e l l " : 4 0 } ] / / o u t p u t { " j a v a S c r i p t " : 2 5 3 7 , " s h e l l " : 4 0 }
  46. REDUCE Also known as fold in other languages [ ]

    . r e d u c e ( r e d u c e r , i n i t i a l V a l u e ) ; / / E S 5 r e d u c e r ( a c c u m u l a t o r , c u r r e n t V a l u e )
  47. EXAMPLE OF REDUCE get the sum of all element in

    a collection [ 1 , 3 , 5 ] . r e d u c e ( f u n c t i o n a d d e r ( s u m , n u m ) { r e t u r n s u m + n u m ; } , 0 ) ; f u n c t i o n a d d e r ( 0 , 1 ) - > r e t u r n 0 + 1 f u n c t i o n a d d e r ( 1 , 3 ) - > r e t u r n 1 + 3 f u n c t i o n a d d e r ( 4 , 5 ) - > r e t u r n 4 + 5 f i n a l r e s u l t : 9
  48. AGGREGATING LANGUAGES R . r e d u c e

    ( f u n c t i o n ( a g g r e g a t e d , l a n g u a g e s ) { R . f o r E a c h ( f u n c t i o n ( l a n g ) { i f ( ! a g g r e g a t e d [ l a n g ] ) a g g r e g a t e d [ l a n g ] = 0 ; a g g r e g a t e d [ l a n g ] + = l a n g u a g e s [ l a n g ] ; } ) ( R . k e y s ( l a n g u a g e s ) ) ; r e t u r n a g g r e g a t e d ; } , { } ) ;
  49. TRANSFORMING THE AGGREGATED OBJECT INTO AN ARRAY / / i

    n p u t { " j a v a S c r i p t " : 2 5 3 7 , " s h e l l " : 4 0 } / / o u t p u t [ { " l a n g u a g e " : " j a v a s c r i p t " , " c o u n t " : 2 5 3 7 } , { " l a n g u a g e " : " s h e l l " , " c o u n t " : 4 0 } ]
  50. TRANSFORMING THE AGGREGATED OBJECT INTO AN ARRAY R . c

    o m p o s e ( R . m a p ( f u n c t i o n ( p a i r ) { r e t u r n { l a n g u a g e : p a i r [ 0 ] , c o u n t : p a i r [ 1 ] } } ) , R . t o P a i r s / / { a : 1 , b : 2 } - > [ [ ' a ' , 1 ] , [ ' b ' , 2 ] ] )
  51. SORTING LANGUAGES / / i n p u t [

    { " l a n g u a g e " : " s h e l l " , " c o u n t " : 4 0 } , { " l a n g u a g e " : " j a v a s c r i p t " , " c o u n t " : 2 5 3 7 } ] / / o u t p u t [ { " l a n g u a g e " : " j a v a s c r i p t " , " c o u n t " : 2 5 3 7 } , { " l a n g u a g e " : " s h e l l " , " c o u n t " : 4 0 } ]
  52. SORTING BY COUNT R . s o r t B

    y ( R . p r o p ( ' c o u n t ' ) ) Sort in increasing order according to a key given by the function R . c o m p o s e ( R . r e v e r s e , R . s o r t B y ( R . p r o p ( ' c o u n t ' ) ) )
  53. R.pCompose \o/ RAMDA WITH PROMISES Same as compose, but if

    something returns a thenable, it'll be handled asynchronously.
  54. PUTTING IT ALL TOGETHER v a r g e t

    L a n g u a g e s = f u n c t i o n ( y e a r / * Y Y Y Y * / ) { r e t u r n R . p C o m p o s e ( s o r t L a n g u a g e s a g g r e g a t e L a n g u a g e s , P r o m i s e . a l l , g e t L a n g u a g e s , c h o o s e B y Y e a r ( y e a r ) , f e t c h R e p o s ) ( ) ; }
  55. v a r c h o o s e B

    y Y e a r = f u n c t i o n ( y e a r ) { r e t u r n R . f i l t e r ( R . c o m p o s e ( R . g t e ( y e a r ) , R . s l i c e ( 0 , 4 ) , R . g e t ( ' u p d a t e d _ a t ' ) ) ) ; }
  56. v a r g e t L a n g

    u a g e s = R . c o m p o s e ( R . m a p ( R . c o m p o s e ( g e t R e p o L a n g u a g e s , R . g e t ( ' l a n g u a g e _ u r l ' ) ) ) , R . f i l t e r ( R . g e t ( ' l a n g u a g e s ' ) ) )
  57. v a r a g g r e g a

    t e L a n g u a g e s = R . r e d u c e ( f u n c t i o n ( a g g r e g a t e d , l a n g u a g e s ) { r e t u r n R . f o r E a c h ( f u n c t i o n ( l a n g ) { i f ( ! a g g r e g a t e d [ l a n g ] ) a g g r e g a t e d [ l a n g ] = 0 ; a g g r e g a t e d [ l a n g ] + = l a n g u a g e s [ l a n g ] ; } ) ( R . k e y s ( l a n g u a g e s ) ) ; } , { } ) ;
  58. v a r s o r t L a n

    g u a g e s = R . c o m p o s e ( R . r e v e r s e , R . s o r t B y ( R . p r o p ( ' c o u n t ' ) ) ) ;
  59. WEIRD ORDER? PIPE AND PPIPE Mirror versions of compose and

    pCompose v a r g e t L a n g u a g e s = f u n c t i o n ( y e a r / * Y Y Y Y * / ) { r e t u r n R . p P i p e ( f e t c h R e p o s , c h o o s e B y Y e a r ( y e a r ) , g e t L a n g u a g e s , P r o m i s e . a l l , a g g r e g a t e L a n g u a g e s , s o r t L a n g u a g e s ) ( ) ; }
  60. ESSENCE OF FUNCTIONAL PROGRAMMING Combine (compose) functions with compose &

    curry Any loop can be expressed with a combination of map, filter, reduce
  61. WHAT DO WE GAIN WITH FP? Simplicity Easier decomposition of

    problems Code more closely tight to problem domain Which leads in turn to: Straightforward unit testing Simple concurrency
  62. CAVEATS Big surface API (similar to underscore/lodash actually) Not enough

    in itself: closures breaks compositions. Avoid states Need immutability FP is weird (at first). Start with map/filter to eliminate for loops
  63. RESOURCES , another FP style library stream based library (by

    the author of async) and , two libraries to bring efficient immutable data structure to js land. Functional programming in js Allong.es highland Immutable Mori
  64. Q&A