├── .editorconfig ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── LICENSE.txt ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── ch11-code ├── fp-helpers.js ├── index.html ├── mock-server.js ├── stock-ticker-events.js ├── stock-ticker.css └── stock-ticker.js └── manuscript ├── README.md ├── apA.md ├── apB.md ├── apC.md ├── ch1.md ├── ch10.md ├── ch11.md ├── ch2.md ├── ch3.md ├── ch4.md ├── ch5.md ├── ch6.md ├── ch7.md ├── ch8.md ├── ch9.md ├── foreword.md ├── images ├── fig1.png ├── fig10.png ├── fig10.svg ├── fig11.png ├── fig11.svg ├── fig12.png ├── fig12.svg ├── fig13.png ├── fig13.svg ├── fig14.png ├── fig14.svg ├── fig15.png ├── fig15.svg ├── fig16.png ├── fig16.svg ├── fig17.png ├── fig17.svg ├── fig18.png ├── fig18.svg ├── fig19.png ├── fig19.svg ├── fig2.png ├── fig2.svg ├── fig3.png ├── fig3.svg ├── fig4.png ├── fig4.svg ├── fig5.png ├── fig5.svg ├── fig6.png ├── fig6.svg ├── fig7.png ├── fig7.svg ├── fig8.png ├── fig8.svg ├── fig9.png ├── fig9.svg └── marketing │ ├── back-cover-hd.png │ ├── fljs-lightbulb.svg │ ├── front-cover-hd.png │ ├── front-cover-hd.tiff │ ├── front-cover-sd.png │ ├── front-cover-small.png │ ├── print-book-cover.pdf │ ├── print-book-cover.png │ ├── social-avatar.png │ └── social-header.png └── preface.md /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = tab 8 | indent_size = 4 9 | 10 | [*.md] 11 | indent_style = space 12 | indent_size = 4 13 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Please feel free to contribute to the quality of this content by submitting PR's for improvements to code snippets, explanations, etc. If there's any doubt or if you think that a word/phrase is used confusingly, **before submitting a PR, open an issue to ask about it.** 4 | 5 | However, if you choose to contribute content (not just typo corrections) to this repo, you agree that you're giving me a non-exclusive license to use that content for the book, as I (and any future publisher) deem appropriate. You probably guessed that already, but I just have to make sure the lawyers are happy by explicitly stating it. 6 | 7 | ## Search First! 8 | 9 | If you have any questions or concerns, please make sure to search the issues (both open and closed!) first, to keep the churn of issues to a minimum. I want to keep my focus on improving the content as much as possible. 10 | 11 | ## Typos? 12 | 13 | This book has already been edited. Most typos have already been caught and fixed, but there might still be some mistakes here and there. 14 | 15 | If you're going to submit a PR for typo fixes, please be measured in doing so, perhaps by collecting several small changes into a single PR (in separate commits). 16 | 17 | ## Reading Experience (Chapter/Section links, etc) 18 | 19 | This repo **is not optimized for your reading experience.** It's optimized for the publishing process. 20 | 21 | The primary intended reading experience -- likely the most pleasant one for many of you! -- is the ebook, [which is now on sale](https://leanpub.com/fljs). The balance I'm striking here is releasing the content for free, but selling the reading experience. Other authors make different decisions on that balance, but that's what I've come to for now. 22 | 23 | I hope you continue to enjoy and benefit from the content, and I also hope you value it enough to purchase the best reading experience in the ebook/print form. 24 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Yes, I promise I've read the [Contributions Guidelines](https://github.com/getify/Functional-Light-JS/blob/master/CONTRIBUTING.md)** (please feel free to remove this line -- if you leave this line here, I'm going to assume you didn't actually read it). 2 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Yes, I promise I've read the [Contributions Guidelines](https://github.com/getify/Functional-Light-JS/blob/master/CONTRIBUTING.md)** (please feel free to remove this line -- if you leave this line here, I'm going to assume you didn't actually read it). 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Functional-Light JavaScript - PTBR 2 | 3 | [![License: CC BY-NC-ND 4.0](https://img.shields.io/badge/License-CC%20BY--NC--ND%204.0-blue.svg)](http://creativecommons.org/licenses/by-nc-nd/4.0/) 4 | 5 | Capa do livro 6 | 7 | Esse livro é uma visão balanceada e pragmatica sobre FP em JavaScript. A primeira edição está completa. Leia online aqui **de graça**, ou: 8 | 9 |

10 | Compre pelo Leanpub Compre pela Amazon 11 |

12 | 13 | O "Functional-Light JavaScript" explora os principios básicos de programação funcional (FP) os aplicando ao JavaScript. Mas, o que torna esse livro diferente é a abordagem a esses principios sem o afundamento em toda a sua terminologia complexa, tratando um sub-conjunto de conceitos de base de programação funcional que chamamos de "Functional-Light Programming" (FLP) e o aplicando em JavaScript. 14 | 15 | **Nota:** Apesar da palavra "Light" no título, Esse livro não é recomedando ou considerado como "iniciante", "fácil", or "introdutório" a este tópico. Este livro é rigoroso e cheio de detalhes complicados sendo necessário uma base sólida de conhecimento JS antes de explorado. "Light" significa limitado em escopo, ao invés de ser mais amplo, este livro vai muito mais profundo em cada tópico que você típicamente acharia em outros livros de Javascript Funcional. 16 | 17 | Convenhamos que a não ser que você já é um membro do grupo descolados da programação funcional (eu não sou!), uma frase como "uma mônada é apenas um monóide na categoria dos endofuntores" não significa nada para nós. 18 | 19 | Não dizendo que esses termos não tem significado ou que programação funcional é ruim por usar eles. Mas assim que você tiver estudado o Functional-Light, você vai provavelmente vai querer estudar FP mais formalmente, e vai certamente ter plena visão do que eles significam. 20 | 21 | Mas o que eu quero é ser capaz de aplicar alguns principios básicos de FP ao seu JavaScript *agora*, pois acredito que vai lhe ajudar a escrever um código melhor e mais fundamentado. 22 | 23 | **Para ler mais sobre a motivação e perspectiva por trás deste livro leia o [Prefácio](manuscript/preface.md).** 24 | 25 | ## Livro 26 | 27 | [Índice](manuscript/README.md/#table-of-contents) 28 | 29 | * [Preâmbulo](manuscript/foreword.md/#foreword) (por [Brian Lonsdorf, aka "Prof Frisby"](https://twitter.com/DrBoolean)) 30 | * [Prefácio](manuscript/preface.md/#preface) 31 | * [Capítulo 1: Porquê Programação Funcional?](manuscript/ch1.md/#Chapter-1-why-functional-programming) 32 | * [Capítulo 2: A Natureza Das Funções](manuscript/ch2.md/#chapter-2-the-nature-of-functions) 33 | * [Capítulo 3: Lidando Com Entradas De Funções](manuscript/ch3.md/#chapter-3-managing-function-inputs) 34 | * [Capítulo 4: Compondo Funções](manuscript/ch4.md/#chapter-4-composing-functions) 35 | * [Capítulo 5: Reduzindo Efeitos Coláterais](manuscript/ch5.md/#chapter-5-reducing-side-effects) 36 | * [Capítulo 6: Imutabilidade De Valores](manuscript/ch6.md/#chapter-6-value-immutability) 37 | * [Capítulo 7: Closure vs Objeto](manuscript/ch7.md/#chapter-7-closure-vs-object) 38 | * [Capítulo 8: Recursão](manuscript/ch8.md/#chapter-8-recursion) 39 | * [Capítulo 9: Operações Em Lista](manuscript/ch9.md/#chapter-9-list-operations) 40 | * [Capítulo 10: Async Funcional](manuscript/ch10.md/#chapter-10-functional-async) 41 | * [Capítulo 11: Juntando Tudo](manuscript/ch11.md/#chapter-11-putting-it-all-together) 42 | * [Appendix A: Transduzindo](manuscript/apA.md/#appendix-a-transducing) 43 | * [Appendix B: A humilde monada](manuscript/apB.md/#appendix-b-the-humble-monad) 44 | * [Appendix C: Bibliotecas FP](manuscript/apC.md/#appendix-c-fp-libraries) 45 | 46 | ## Tradução 47 | 48 | Este livro ainda está sendo traduzido para o português pelo fork do [Lambda Study Group](https://github.com/lambda-study-group/Functional-Light-JS)! 49 | Para colaborar você pode criar uma issue especificando quais partes você quer traduzir e em seguida enviar o PR referenciando a issue. 50 | 51 | ## Publicação 52 | 53 | Esse livro foi publicado [e está disponível para compra no Leanpub](https://leanpub.com/fljs/). 54 | [![Compre no Leanpub](https://img.shields.io/badge/Buy-Leanpub-yellow.svg)](https://leanpub.com/fljs) 55 | 56 | Se você pretende contribuir financeiramente além da compra do livro, eu tenho um [patreon](https://www.patreon.com/getify) o qual vou sempre apreciar sua generosidade. 57 | 58 | [![patreon.png](https://s13.postimg.org/k9nkc5thz/become_a_patron_button.png)](https://www.patreon.com/getify) 59 | 60 | ## In-person Training 61 | 62 | O conteúdo desse livro vem fortemente de um workshop de treinamento o qual eu ensino profissionalmente (tanto públicamente quando em ambientes corporativos privados) do mesmo nome do livro. 63 | 64 | Se você gostou desse conteúdo e gostaria de me contatar, considerando que conduzo esse tipo de treinamento ou de diversos tópicos como JS/HTML5/Node.js, utilize o email: getify @ gmail 65 | 66 | ## Treinamento Online em Video 67 | 68 | Eu também tenho diversos cursos de treinamento JS disponíveis em formato vídeo. Eu [ensino cursos](https://FrontendMasters.com/teachers/kyle-simpson) pelo [Frontend Masters](https://FrontendMasters.com), como o [Functional-Light JavaScript v2](https://frontendmasters.com/courses/functional-javascript-v2/) workshop. Alguns de meus cursos também estão disponível pelo [PluralSight](https://www.pluralsight.com/search?q=kyle%20simpson&categories=all). 69 | 70 | ## Contribuição 71 | 72 | Qualquer contribuição será **obviamente muito bem apreciada**. 73 | 74 | Mas **POR FAVOR** leia o [Guia de contribuições](CONTRIBUTING.md) com cuidado antes de enviar um PR. 75 | 76 | ## Licensa & Copyright 77 | 78 | The materials herein are all (c) 2016-2018 Kyle Simpson. 79 | 80 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 4.0 Unported License. 81 | -------------------------------------------------------------------------------- /ch11-code/fp-helpers.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var pipe = reverseArgs(compose); 4 | 5 | // curried list operators 6 | var map = unboundMethod( "map", 2 ); 7 | var filter = unboundMethod( "filter", 2 ); 8 | var filterIn = filter; 9 | var reduce = unboundMethod( "reduce", 3 ); 10 | var each = unboundMethod( "forEach", 2 ); 11 | var flatMap = curry( function flatMap(mapperFn,arr) { 12 | return arr.reduce( function reducer(list,v) { 13 | return list.concat( mapperFn( v ) ); 14 | }, [] ); 15 | } ); 16 | 17 | 18 | // ************************************ 19 | 20 | function filterOut(predicateFn,arr) { 21 | return filterIn( not( predicateFn ), arr ); 22 | } 23 | 24 | function unary(fn) { 25 | return function onlyOneArg(arg){ 26 | return fn( arg ); 27 | }; 28 | } 29 | 30 | function not(predicate) { 31 | return function negated(...args){ 32 | return !predicate( ...args ); 33 | }; 34 | } 35 | 36 | function reverseArgs(fn) { 37 | return function argsReversed(...args){ 38 | return fn( ...args.reverse() ); 39 | }; 40 | } 41 | 42 | function spreadArgs(fn) { 43 | return function spreadFn(argsArr){ 44 | return fn( ...argsArr ); 45 | }; 46 | } 47 | 48 | function partial(fn,...presetArgs) { 49 | return function partiallyApplied(...laterArgs){ 50 | return fn( ...presetArgs, ...laterArgs ); 51 | }; 52 | } 53 | 54 | function partialRight(fn,...presetArgs) { 55 | return function partiallyApplied(...laterArgs){ 56 | return fn( ...laterArgs, ...presetArgs ); 57 | }; 58 | } 59 | 60 | function curry(fn,arity = fn.length) { 61 | return (function nextCurried(prevArgs){ 62 | return function curried(nextArg){ 63 | var args = [ ...prevArgs, nextArg ]; 64 | 65 | if (args.length >= arity) { 66 | return fn( ...args ); 67 | } 68 | else { 69 | return nextCurried( args ); 70 | } 71 | }; 72 | })( [] ); 73 | } 74 | 75 | function uncurry(fn) { 76 | return function uncurried(...args){ 77 | var ret = fn; 78 | 79 | for (let i = 0; i < args.length; i++) { 80 | ret = ret( args[i] ); 81 | } 82 | 83 | return ret; 84 | }; 85 | } 86 | 87 | function zip(arr1,arr2) { 88 | var zipped = []; 89 | arr1 = [...arr1]; 90 | arr2 = [...arr2]; 91 | 92 | while (arr1.length > 0 && arr2.length > 0) { 93 | zipped.push( [ arr1.shift(), arr2.shift() ] ); 94 | } 95 | 96 | return zipped; 97 | } 98 | 99 | function compose(...fns) { 100 | return fns.reduceRight( function reducer(fn1,fn2){ 101 | return function composed(...args){ 102 | return fn2( fn1( ...args ) ); 103 | }; 104 | } ); 105 | } 106 | 107 | function prop(name,obj) { 108 | return obj[name]; 109 | } 110 | 111 | function setProp(name,obj,val) { 112 | var o = Object.assign( {}, obj ); 113 | o[name] = val; 114 | return o; 115 | } 116 | 117 | function unboundMethod(methodName,argCount = 2) { 118 | return curry( 119 | (...args) => { 120 | var obj = args.pop(); 121 | return obj[methodName]( ...args ); 122 | }, 123 | argCount 124 | ); 125 | } 126 | -------------------------------------------------------------------------------- /ch11-code/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Functional-Light JavaScript, Ch11: Stock Ticker 6 | 7 | 8 | 9 |

Functional-Light JavaScript, Ch11: Stock Ticker

10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ch11-code/mock-server.js: -------------------------------------------------------------------------------- 1 | function connectToServer() { 2 | // faking an event emitter attached to a server-event stream 3 | return evtEmitter; 4 | } 5 | 6 | 7 | // *********************************** 8 | // MOCK SERVER 9 | // *********************************** 10 | 11 | // simple/mock event emitter 12 | var evtEmitter = { 13 | handlers: {}, 14 | on(evtName,cb) { 15 | this.handlers[evtName] = this.handlers[evtName] || []; 16 | this.handlers[evtName][this.handlers[evtName].length] = cb; 17 | }, 18 | addEventListener(...args) { 19 | return this.on( ...args ); 20 | }, 21 | removeEventListener(){}, 22 | emit(evtName,...args) { 23 | for (let handler of (this.handlers[evtName] || [])) { 24 | handler(...args); 25 | } 26 | } 27 | }; 28 | 29 | var stocks = { 30 | "AAPL": { price: 121.95, change: 0.01 }, 31 | "MSFT": { price: 65.78, change: 1.51 }, 32 | "GOOG": { price: 821.31, change: -8.84 }, 33 | }; 34 | 35 | setTimeout( function initialStocks(){ 36 | for (let id in stocks) { 37 | // !!SIDE EFFECTS!! 38 | evtEmitter.emit( "stock", Object.assign( { id }, stocks[id] ) ); 39 | } 40 | }, 100 ); 41 | 42 | setTimeout( function randomStockUpdate(){ 43 | var stockIds = Object.keys( stocks ); 44 | var stockIdx = randInRange( 0, stockIds.length - 1 ); 45 | var change = (randInRange( 1, 10 ) > 7 ? -1 : 1) * 46 | (randInRange( 1, 10 ) / 1E2); 47 | 48 | var newStock = Object.assign( stocks[stockIds[stockIdx]] ); 49 | newStock.price += change; 50 | newStock.change += change; 51 | 52 | // !!SIDE EFFECTS!! 53 | stocks[stockIdx[stockIdx]] = newStock; 54 | evtEmitter.emit( "stock-update", Object.assign( { id: stockIds[stockIdx] }, newStock ) ); 55 | 56 | setTimeout( randomStockUpdate, randInRange( 300, 1500 ) ); 57 | }, 1000 ); 58 | 59 | 60 | // !!SIDE EFFECTS!! 61 | function randInRange(min = 0,max = 1E9) { 62 | return (Math.round(Math.random() * 1E4) % (max - min)) + min; 63 | } 64 | -------------------------------------------------------------------------------- /ch11-code/stock-ticker-events.js: -------------------------------------------------------------------------------- 1 | var server = connectToServer(); 2 | 3 | var formatDecimal = unboundMethod( "toFixed" )( 2 ); 4 | var formatPrice = pipe( formatDecimal, formatCurrency ); 5 | var formatChange = pipe( formatDecimal, formatSign ); 6 | var processNewStock = pipe( addStockName, formatStockNumbers ); 7 | var observableMapperFns = [ processNewStock, formatStockNumbers ]; 8 | var makeObservableFromEvent = curry( Rx.Observable.fromEvent, 2 )( server ); 9 | var mapObservable = uncurry( map ); 10 | 11 | var stockEventNames = [ "stock", "stock-update" ]; 12 | 13 | var [ newStocks, stockUpdates ] = pipe( 14 | map( makeObservableFromEvent ), 15 | curry( zip )( observableMapperFns ), 16 | map( spreadArgs( mapObservable ) ) 17 | ) 18 | ( stockEventNames ); 19 | 20 | 21 | // ********************* 22 | 23 | function addStockName(stock) { 24 | return setProp( "name", stock, stock.id ); 25 | } 26 | 27 | function formatStockNumbers(stock) { 28 | var stockDataUpdates = [ 29 | [ "price", formatPrice( stock.price ) ], 30 | [ "change", formatChange( stock.change ) ] 31 | ]; 32 | 33 | return reduce( function formatter(stock,[propName,val]){ 34 | return setProp( propName, stock, val ); 35 | } ) 36 | ( stock ) 37 | ( stockDataUpdates ); 38 | } 39 | 40 | function formatSign(val) { 41 | if (Number(val) > 0) { 42 | return `+${val}`; 43 | } 44 | return val; 45 | } 46 | 47 | function formatCurrency(val) { 48 | return `$${val}`; 49 | } 50 | -------------------------------------------------------------------------------- /ch11-code/stock-ticker.css: -------------------------------------------------------------------------------- 1 | *, *:before, *:after { 2 | -moz-box-sizing: border-box; 3 | box-sizing: border-box; 4 | } 5 | 6 | #stock-ticker { 7 | background-color: #efefef; 8 | padding: 1em; 9 | margin: 0; 10 | width: 14em; 11 | } 12 | 13 | .stock { 14 | list-style-type: none; 15 | padding: 0; 16 | margin: 0 0 1em 0; 17 | } 18 | 19 | .stock:last-child { 20 | margin-bottom: 0; 21 | } 22 | 23 | .stock > span { 24 | display: inline-block; 25 | width: 4em; 26 | text-align: right; 27 | overflow: hidden; 28 | } 29 | 30 | .stock-name { 31 | font-weight: bold; 32 | text-align: center; 33 | } 34 | 35 | -------------------------------------------------------------------------------- /ch11-code/stock-ticker.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var stockTickerUI = { 4 | 5 | updateStockElems(stockInfoChildElemList,data) { 6 | var getDataVal = curry( reverseArgs( prop ), 2 )( data ); 7 | var extractInfoChildElemVal = pipe( 8 | getClassName, 9 | curry( stripPrefix )( /\bstock-/i ), 10 | getDataVal 11 | ); 12 | var orderedDataVals = 13 | map( extractInfoChildElemVal )( stockInfoChildElemList ); 14 | var elemsValsTuples = 15 | filterOut( function updateValueMissing([infoChildElem,val]){ 16 | return val === undefined; 17 | } ) 18 | ( zip( stockInfoChildElemList, orderedDataVals ) ); 19 | 20 | // !!SIDE EFFECTS!! 21 | compose( each, spreadArgs )( setDOMContent ) 22 | ( elemsValsTuples ); 23 | }, 24 | 25 | updateStock(tickerElem,data) { 26 | var getStockElemFromId = curry( getStockElem )( tickerElem ); 27 | var stockInfoChildElemList = pipe( 28 | getStockElemFromId, 29 | getStockInfoChildElems 30 | ) 31 | ( data.id ); 32 | 33 | return stockTickerUI.updateStockElems( 34 | stockInfoChildElemList, 35 | data 36 | ); 37 | }, 38 | 39 | addStock(tickerElem,data) { 40 | var [stockElem, ...infoChildElems] = map( 41 | createElement 42 | ) 43 | ( [ "li", "span", "span", "span" ] ); 44 | var attrValTuples = [ 45 | [ ["class","stock"], ["data-stock-id",data.id] ], 46 | [ ["class","stock-name"] ], 47 | [ ["class","stock-price"] ], 48 | [ ["class","stock-change"] ] 49 | ]; 50 | var elemsAttrsTuples = 51 | zip( [stockElem, ...infoChildElems], attrValTuples ); 52 | 53 | // !!SIDE EFFECTS!! 54 | each( function setElemAttrs([elem,attrValTupleList]){ 55 | each( 56 | spreadArgs( partial( setElemAttr, elem ) ) 57 | ) 58 | ( attrValTupleList ); 59 | } ) 60 | ( elemsAttrsTuples ); 61 | 62 | // !!SIDE EFFECTS!! 63 | stockTickerUI.updateStockElems( infoChildElems, data ); 64 | reduce( appendDOMChild )( stockElem )( infoChildElems ); 65 | appendDOMChild( tickerElem, stockElem ); 66 | } 67 | 68 | }; 69 | 70 | var getDOMChildren = pipe( 71 | listify, 72 | flatMap( 73 | pipe( 74 | curry( prop )( "childNodes" ), 75 | Array.from 76 | ) 77 | ) 78 | ); 79 | var createElement = document.createElement.bind( document ); 80 | var getElemAttrByName = curry( getElemAttr, 2 ); 81 | var getStockId = getElemAttrByName( "data-stock-id" ); 82 | var getClassName = getElemAttrByName( "class" ); 83 | var isMatchingStock = curry( matchingStockId, 2 ); 84 | var ticker = document.getElementById( "stock-ticker" ); 85 | var stockTickerUIMethodsWithDOMContext = map( 86 | pipe( partialRight, unary )( partial, ticker ) 87 | ) 88 | ( [ stockTickerUI.addStock, stockTickerUI.updateStock ] ); 89 | var subscribeToObservable = 90 | pipe( unboundMethod, uncurry )( "subscribe" ); 91 | var stockTickerObservables = [ newStocks, stockUpdates ]; 92 | 93 | // !!SIDE EFFECTS!! 94 | each( spreadArgs( subscribeToObservable ) ) 95 | ( zip( stockTickerUIMethodsWithDOMContext, stockTickerObservables ) ); 96 | 97 | 98 | // ********************* 99 | 100 | function stripPrefix(prefixRegex,val) { 101 | return val.replace( prefixRegex, "" ); 102 | } 103 | 104 | function listify(listOrItem) { 105 | if (!Array.isArray( listOrItem )) { 106 | return [ listOrItem ]; 107 | } 108 | return listOrItem; 109 | } 110 | 111 | function isTextNode(node) { 112 | return node && node.nodeType == 3; 113 | } 114 | 115 | function getElemAttr(prop,elem) { 116 | return elem.getAttribute( prop ); 117 | } 118 | 119 | function setElemAttr(elem,prop,val) { 120 | // !!SIDE EFFECTS!! 121 | return elem.setAttribute( prop, val ); 122 | } 123 | 124 | function matchingStockId(id,node) { 125 | return getStockId( node ) == id; 126 | } 127 | 128 | function isStockInfoChildElem(elem) { 129 | return /\bstock-/i.test( getClassName( elem ) ); 130 | } 131 | 132 | function getStockElem(tickerElem,stockId) { 133 | return pipe( 134 | getDOMChildren, 135 | filterOut( isTextNode ), 136 | filterIn( isMatchingStock( stockId ) ) 137 | ) 138 | ( tickerElem ); 139 | } 140 | 141 | function getStockInfoChildElems(stockElem) { 142 | return pipe( 143 | getDOMChildren, 144 | filterOut( isTextNode ), 145 | filterIn( isStockInfoChildElem ) 146 | ) 147 | ( stockElem ); 148 | } 149 | 150 | function appendDOMChild(parentNode,childNode) { 151 | // !!SIDE EFFECTS!! 152 | parentNode.appendChild( childNode ); 153 | return parentNode; 154 | } 155 | 156 | function setDOMContent(elem,html) { 157 | // !!SIDE EFFECTS!! 158 | elem.innerHTML = html; 159 | return elem; 160 | } 161 | -------------------------------------------------------------------------------- /manuscript/README.md: -------------------------------------------------------------------------------- 1 | # Functional-Light JavaScript 2 | 3 | 4 | 5 | [![Buy on Leanpub](https://img.shields.io/badge/Buy-Leanpub-yellow.svg)](https://leanpub.com/fljs) [![Buy on Amazon](https://img.shields.io/badge/Buy-Amazon-yellow.svg)](http://amazon.fljsbook.com) 6 | 7 | ## Table of Contents 8 | 9 | * [Foreword](foreword.md/#foreword) 10 | * [Preface](preface.md/#preface) 11 | * [Chapter 1: Why Functional Programming?](ch1.md/#chapter-1-why-functional-programming) 12 | * [At a Glance](ch1.md/#at-a-glance) 13 | * [Confidence](ch1.md/#confidence) 14 | * [Communication](ch1.md/#communication) 15 | * [Readability](ch1.md/#readability) 16 | * [Perspective](ch1.md/#perspective) 17 | * [How to Find Balance](ch1.md/#how-to-find-balance) 18 | * [Resources](ch1.md/#resources) 19 | * [Chapter 2: The Nature Of Functions](ch2.md/#chapter-2-the-nature-of-functions) 20 | * [What Is A Function?](ch2.md/#what-is-a-function) 21 | * [Function Input](ch2.md/#function-input) 22 | * [Named Arguments](ch2.md/#named-arguments) 23 | * [Function Output](ch2.md/#function-output) 24 | * [Functions Of Functions](ch2.md/#functions-of-functions) 25 | * [Syntax](ch2.md/#syntax) 26 | * [What's This?](ch2.md/#whats-this) 27 | * [Chapter 3: Managing Function Inputs](ch3.md/#chapter-3-managing-function-inputs) 28 | * [All For One](ch3.md/#all-for-one) 29 | * [Adapting Arguments to Parameters](ch3.md/#adapting-arguments-to-parameters) 30 | * [Some Now, Some Later](ch3.md/#some-now-some-later) 31 | * [One At A Time](ch3.md/#one-at-a-time) 32 | * [Order Matters](ch3.md/#order-matters) 33 | * [No Points](ch3.md/#no-points) 34 | * [Chapter 4: Composing Functions](ch4.md/#chapter-4-composing-functions) 35 | * [Output To Input](ch4.md/#output-to-input) 36 | * [General Composition](ch4.md/#general-composition) 37 | * [Reordered Composition](ch4.md/#reordered-composition) 38 | * [Abstraction](ch4.md/#abstraction) 39 | * [Revisiting Points](ch4.md/#revisiting-points) 40 | * [Chapter 5: Reducing Side Effects](ch5.md/#chapter-5-reducing-side-effects) 41 | * [Effects On The Side, Please](ch5.md/#effects-on-the-side-please) 42 | * [Once Is Enough, Thanks](ch5.md/#once-is-enough-thanks) 43 | * [Pure Bliss](ch5.md/#pure-bliss) 44 | * [There Or Not](ch5.md/#there-or-not) 45 | * [Purifying](ch5.md/#purifying) 46 | * [Chapter 6: Value Immutability](ch6.md/#chapter-6-value-immutability) 47 | * [Primitive Immutability](ch6.md/#primitive-immutability) 48 | * [Value To Value](ch6.md/#value-to-value) 49 | * [Reassignment](ch6.md/#reassignment) 50 | * [Performance](ch6.md/#performance) 51 | * [Treatment](ch6.md/#treatment) 52 | * [Chapter 7: Closure vs Object](ch7.md/#chapter-7-closure-vs-object) 53 | * [The Same Page](ch7.md/#the-same-page) 54 | * [Look Alike](ch7.md/#look-alike) 55 | * [Two Roads Diverged In A Wood...](ch7.md/#two-roads-diverged-in-a-wood) 56 | * [Chapter 8: Recursion](ch8.md/#chapter-8-recursion) 57 | * [Definition](ch8.md/#definition) 58 | * [Declarative Recursion](ch8.md/#declarative-recursion) 59 | * [Stack](ch8.md/#stack) 60 | * [Rearranging Recursion](ch8.md/#rearranging-recursion) 61 | * [Chapter 9: List Operations](ch9.md/#chapter-9-list-operations) 62 | * [Non-FP List Processing](ch9.md/#non-fp-list-processing) 63 | * [Map](ch9.md/#map) 64 | * [Filter](ch9.md/#filter) 65 | * [Reduce](ch9.md/#reduce) 66 | * [Advanced List Operations](ch9.md/#advanced-list-operations) 67 | * [Method vs. Standalone](ch9.md/#method-vs-standalone) 68 | * [Looking For Lists](ch9.md/#looking-for-lists) 69 | * [Fusion](ch9.md/#fusion) 70 | * [Beyond Lists](ch9.md/#beyond-lists) 71 | * [Chapter 10: Functional Async](ch10.md/#chapter-10-functional-async) 72 | * [Time As State](ch10.md/#time-as-state) 73 | * [Eager vs Lazy](ch10.md/#eager-vs-lazy) 74 | * [Reactive FP](ch10.md/#reactive-fp) 75 | * [Chapter 11: Putting It All Together](ch11.md/#chapter-11-putting-it-all-together) 76 | * [Setup](ch11.md/#setup) 77 | * [Stock Events](ch11.md/#stock-events) 78 | * [Stock Ticker UI](ch11.md/#stock-ticker-ui) 79 | * [Appendix A: Transducing](apA.md/#appendix-a-transducing) 80 | * [Why, First](apA.md/#why-first) 81 | * [How, Next](apA.md/#how-next) 82 | * [What, Finally](apA.md/#what-finally) 83 | * [Appendix B: The Humble Monad](apB.md/#appendix-b-the-humble-monad) 84 | * [Type](apB.md/#type) 85 | * [Loose Interface](apB.md/#loose-interface) 86 | * [Just a Monad](apB.md/#just-a-monad) 87 | * [Maybe](apB.md/#maybe) 88 | * [Humble](apB.md/#humble) 89 | * [Appendix C: FP Libraries](apC.md/#appendix-c-fp-libraries) 90 | * [Stuff to Investigate](apC.md/#stuff-to-investigate) 91 | * [Ramda](apC.md/#ramda-0230) 92 | * [Lodash/fp](apC.md/#lodashfp-4174) 93 | * [Mori](apC.md/#mori-032) 94 | * [Bonus: FPO](apC.md/#bonus-fpo) 95 | * [Bonus #2: fasy](apC.md/#bonus-2-fasy) 96 | -------------------------------------------------------------------------------- /manuscript/apC.md: -------------------------------------------------------------------------------- 1 | # Functional-Light JavaScript 2 | # Appendix C: FP Libraries 3 | 4 | If you've been reading this book from start to finish, take a minute to stop and look back how far you've come since [Chapter 1](ch1.md). It's been quite a journey. I hope you've learned a lot and gained insight into thinking functionally for your own programs. 5 | 6 | I want to close this book leaving you with some quick pointers of working with common/popular FP libraries. This is not an exhaustive documentation on each, but a quick glance at the things you should be aware of as you venture beyond "Functional-Light" into broader FP. 7 | 8 | Wherever possible, I recommend you *not* reinvent any wheels. If you find an FP library that suits your needs, use it. Only use the ad hoc helper utilities from this book -- or invent ones of your own! -- if you can't find a suitable library method for your circumstance. 9 | 10 | ## Stuff to Investigate 11 | 12 | Let's expand the [list of FP libraries to be aware of, from Chapter 1](ch1.md/#libraries). We won't cover all of these (as there's a lot of overlap), but here are the ones that should probably be on your radar screen: 13 | 14 | * [Ramda](http://ramdajs.com): General FP Utilities 15 | * [Sanctuary](https://github.com/sanctuary-js/sanctuary): Ramda Companion for FP Types 16 | * [lodash/fp](https://github.com/lodash/lodash/wiki/FP-Guide): General FP Utilities 17 | * [functional.js](http://functionaljs.com/): General FP Utilities 18 | * [Immutable](https://github.com/facebook/immutable-js): Immutable Data Structures 19 | * [Mori](https://github.com/swannodette/mori): (ClojureScript Inspired) Immutable Data Structures 20 | * [Seamless-Immutable](https://github.com/rtfeldman/seamless-immutable): Immutable Data Helpers 21 | * [transducers-js](https://github.com/cognitect-labs/transducers-js): Transducers 22 | * [monet.js](https://github.com/monet/monet.js): Monadic Types 23 | 24 | There are dozens of other fine libraries not on this list. Just because it's not on my list here doesn't mean it's not good, nor is this list a particular endorsement. It's just a quick glance at the landscape of FP-in-JavaScript. A much longer list of FP resources can be [found here](https://github.com/stoeffel/awesome-fp-js). 25 | 26 | One resource that's extremely important to the FP world -- it's not a library but more an encyclopedia! -- is [Fantasy Land](https://github.com/fantasyland/fantasy-land) (aka FL). 27 | 28 | This is definitely not light reading for the faint of heart. It's a complete detailed roadmap of all of FP as it's interpreted in JavaScript. FL has become a de facto standard for JavaScript FP libraries to adhere to, to ensure maximum interoperability. 29 | 30 | Fantasy Land is pretty much the exact opposite of "Functional-Light". It's the full-on no-holds-barred approach to FP in JavaScript. That said, as you venture beyond this book, it's likely that FL will be on that road for you. I'd recommend you bookmark it, and go back to it after you've had at least six months of real-world practice with this book's concepts. 31 | 32 | ## Ramda (0.23.0) 33 | 34 | From the [Ramda documentation](http://ramdajs.com/): 35 | 36 | > Ramda functions are automatically curried. 37 | > 38 | > The parameters to Ramda functions are arranged to make it convenient for currying. The data to be operated on is generally supplied last. 39 | 40 | I find that design decision to be one of Ramda's strengths. It's also important to note that Ramda's form of currying (as with most libraries, it seems) is the ["loose currying" we talked about in Chapter 3](ch3.md/#user-content-loosecurry). 41 | 42 | The [final example of Chapter 3](ch3.md/#user-content-finalshortlong) -- recall defining a point-free `printIf(..)` utility -- can be done with Ramda like this: 43 | 44 | ```js 45 | function output(msg) { 46 | console.log( msg ); 47 | } 48 | 49 | function isShortEnough(str) { 50 | return str.length <= 5; 51 | } 52 | 53 | var isLongEnough = R.complement( isShortEnough ); 54 | 55 | var printIf = R.partial( R.flip( R.when ), [output] ); 56 | 57 | var msg1 = "Hello"; 58 | var msg2 = msg1 + " World"; 59 | 60 | printIf( isShortEnough, msg1 ); // Hello 61 | printIf( isShortEnough, msg2 ); 62 | 63 | printIf( isLongEnough, msg1 ); 64 | printIf( isLongEnough, msg2 ); // Hello World 65 | ``` 66 | 67 | A few differences to point out compared to [Chapter 3's approach](ch3.md/#user-content-finalshortlong): 68 | 69 | * We use `R.complement(..)` instead of `not(..)` to create a negating function `isLongEnough(..)` around `isShortEnough(..)`. 70 | 71 | * We use `R.flip(..)` instead of `reverseArgs(..)`. It's important to note that `R.flip(..)` only swaps the first two arguments, whereas `reverseArgs(..)` reverses all of them. In this case, `flip(..)` is more convenient for us, so we don't need to do `partialRight(..)` or any of that kind of juggling. 72 | 73 | * `R.partial(..)` takes all of its subsequent arguments (beyond the function) as a single array. 74 | 75 | * Because Ramda is using loose currying, we don't need to use `R.uncurryN(..)` to get a `printIf(..)` that takes both its arguments. If we did, it would look like `R.uncurryN( 2, .. )` wrapped around the `R.partial(..)` call; but that's not necessary. 76 | 77 | Ramda is a very popular and powerful library. It's a really good place to start if you're practicing adding FP to your code base. 78 | 79 | ## Lodash/fp (4.17.4) 80 | 81 | Lodash is one of the most popular libraries in the entire JS ecosystem. They publish an "FP-friendly" version of their API as ["lodash/fp"](https://github.com/lodash/lodash/wiki/FP-Guide). 82 | 83 | In [Chapter 9, we looked at composing standalone list operations](ch9.md/#composing-standalone-utilities) (`map(..)`, `filter(..)`, and `reduce(..)`). Here's how we could do it with "lodash/fp": 84 | 85 | ```js 86 | var sum = (x,y) => x + y; 87 | var double = x => x * 2; 88 | var isOdd = x => x % 2 == 1; 89 | 90 | fp.compose( [ 91 | fp.reduce( sum )( 0 ), 92 | fp.map( double ), 93 | fp.filter( isOdd ) 94 | ] ) 95 | ( [1,2,3,4,5] ); // 18 96 | ``` 97 | 98 | Instead of the more familiar `_.` namespace prefix, "lodash/fp" defines its methods with `fp.` as the namespace prefix. I find that a helpful distinguisher, and also generally more easy on my eyes than `_.` anyway! 99 | 100 | Notice that `fp.compose(..)` (also known as `_.flowRight(..)` in lodash proper) takes an array of functions instead of individual arguments. 101 | 102 | You cannot beat the stability, widespread community support, and performance of lodash. It's a solid bet for your FP explorations. 103 | 104 | ## Mori (0.3.2) 105 | 106 | In [Chapter 6](ch6.md), we already briefly glanced at the Immutable.js library, probably the most well-known for immutable data structures. 107 | 108 | Let's instead look at another popular library: [Mori](https://github.com/swannodette/mori). Mori is designed with a different (ostensibly more FP-like) take on API: it uses standalone functions instead of methods directly on the values. 109 | 110 | ```js 111 | var state = mori.vector( 1, 2, 3, 4 ); 112 | 113 | var newState = mori.assoc( 114 | mori.into( state, Array.from( {length: 39} ) ), 115 | 42, 116 | "meaning of life" 117 | ); 118 | 119 | state === newState; // false 120 | 121 | mori.get( state, 2 ); // 3 122 | mori.get( state, 42 ); // undefined 123 | 124 | mori.get( newState, 2 ); // 3 125 | mori.get( newState, 42 ); // "meaning of life" 126 | 127 | mori.toJs( newState ).slice( 1, 3 ); // [2,3] 128 | ``` 129 | 130 | Some interesting things to point out about Mori for this example: 131 | 132 | * We're using a `vector` instead of a `list` (as one might assume), mostly because the documentation says it behaves more like we expect JS arrays to be. 133 | 134 | * We cannot just randomly set a position past the end of the vector like we can with JS arrays; that throws an exception. So we have to first "grow" the vector using `mori.into(..)` with an array of the appropriate size of extra slots we want. Once we have a vector with 43 slots (4 + 39), we can set the final slot (position `42`) to the `"meaning of life"` value using the `mori.assoc(..)` method. 135 | 136 | * The intermediate step of creating a larger vector with `mori.into(..)` and then creating another from it with `mori.assoc(..)` might sound inefficient. But the beauty of immutable data structures is that no cloning is going on here. Each time a "change" is made, the new data structure is just tracking the difference from the previous state. 137 | 138 | Mori is heavily inspired by ClojureScript. Its API will be very familiar if you have experience (or currently work in!) that language. Since I don't have that experience, I find the method names a little strange to get used to. 139 | 140 | But I really like the standalone function design instead of methods on values. Mori also has some functions that automatically return regular JS arrays, which is a nice convenience. 141 | 142 | ## Bonus: FPO 143 | 144 | In [Chapter 2, we introduced a pattern](ch2.md/#named-arguments) for dealing with arguments called "named arguments", which in JS means using an object at the call-site to map properties to destructured function parameters: 145 | 146 | ```js 147 | function foo( {x,y} = {} ) { 148 | console.log( x, y ); 149 | } 150 | 151 | foo( { 152 | y: 3 153 | } ); // undefined 3 154 | ``` 155 | 156 | Then in [Chapter 3, we talked about extending](ch3.md/#order-matters) our ideas of currying and partial application to work with named arguments, like this: 157 | 158 | ```js 159 | function foo({ x, y, z } = {}) { 160 | console.log( `x:${x} y:${y} z:${z}` ); 161 | } 162 | 163 | var f1 = curryProps( foo, 3 ); 164 | 165 | f1( {y: 2} )( {x: 1} )( {z: 3} ); 166 | ``` 167 | 168 | One major benefit of this style is being able to pass arguments (even with currying or partial application!) in any order without needing to do `reverseArgs(..)`-style juggling of parameters. Another is being able to omit an optional argument by simply not specifying it, instead of passing an ugly placeholder. 169 | 170 | In my journey learning FP, I've regularly been frustrated by both of those irritations of functions with traditional positional arguments; thus I've really appreciated the named arguments style for addressing those concerns. 171 | 172 | One day, I was musing about with this style of FP coding, and wondered what it would be like if a whole FP library had all its API methods exposed in this style. I started experimenting, showed those experiments to a few FP folks, and got some positive feedback. 173 | 174 | From those experiments, eventually the [FPO](https://github.com/getify/fpo) (pronounced "eff-poh") library was born; FPO stands for FP-with-Objects, in case you were wondering. 175 | 176 | From the documentation: 177 | 178 | ```js 179 | // Ramda's `reduce(..)` 180 | R.reduce( 181 | (acc,v) => acc + v, 182 | 0, 183 | [3,7,9] 184 | ); // 19 185 | 186 | // FPO named-argument method style 187 | FPO.reduce({ 188 | arr: [3,7,9], 189 | fn: ({acc,v}) => acc + v 190 | }); // 19 191 | ``` 192 | 193 | With traditional library implementations of `reduce(..)` (like Ramda), the initial value parameter is in the middle, and not optional. FPO's `reduce(..)` method can take the arguments in any order, and you can omit the optional initial value if desired. 194 | 195 | As with most other FP libraries, FPO's API methods are automatically loose-curried, so you can not only provide arguments in any order, but specialize the function by providing its arguments over multiple calls: 196 | 197 | ```js 198 | var f = FPO.reduce({ arr: [3,7,9] }); 199 | 200 | // later 201 | 202 | f({ fn: ({acc,v}) => acc + v }); // 19 203 | ``` 204 | 205 | Lastly, all of FPO's API methods are also exposed using the traditional positional arguments style -- you'll find they're all very similar to Ramda and other libraries -- under the `FPO.std.*` namespace: 206 | 207 | ```js 208 | FPO.std.reduce( 209 | (acc,v) => acc + v, 210 | undefined, 211 | [3,7,9] 212 | ); // 19 213 | ``` 214 | 215 | If FPO's named argument form of FP appeals to you, perhaps check out the library and see what you think. It has a full test suite and most of the major FP functionality you'd expect, including everything we covered in this text to get you up and going with Functional-Light JavaScript! 216 | 217 | ## Bonus #2: fasy 218 | 219 | FP iterations (`map(..)`, `filter(..)`, etc.) are almost always modeled as synchronous operations, meaning we eagerly run through all the steps of the iteration immediately. As a matter of fact, other FP patterns like composition and even transducing are also iterations, and are also modeled exactly this way. 220 | 221 | But what happens if one or more of the steps in an iteration needs to complete asynchronously? You might jump to thinking that Observables (see [Chapter 10](ch10.md/#observables)) is the natural answer, but they're not what we need. 222 | 223 | Let me quickly illustrate. 224 | 225 | Imagine you have a list of URLs that represent images you want to load into a web page. The fetching of the images is asynchronous, obviously. So, this isn't going to work quite like you'd hope: 226 | 227 | ```js 228 | var imageURLs = [ 229 | "https://some.tld/image1.png", 230 | "https://other.tld/image2.png", 231 | "https://various.tld/image3.png" 232 | ]; 233 | 234 | var images = imageURLs.map( fetchImage ); 235 | ``` 236 | 237 | The `images` array won't contain the images. Depending on the behavior of `fetchImage(..)`, it probably returns a promise for the image object once it finishes downloading. So `images` would now be a list of promises. 238 | 239 | Of course, you could then use `Promise.all(..)` to wait for all those promises to resolve, and then unwrap an array of the image object results at its completion: 240 | 241 | ```js 242 | Promise.all( images ) 243 | .then(function allImages(imgObjs){ 244 | // .. 245 | }); 246 | ``` 247 | 248 | Unfortunately, this "trick" only works if you're going to do all the asynchronous steps concurrently (rather than serially, one after the other), and only if the operation is a `map(..)` call as shown. If you want serial asynchrony, or you want to, for example, do a `filter(..)` concurrently, this won't quite work; it's possible, but it's messier. 249 | 250 | And some operations naturally require serial asynchrony, like for example an asynchronous `reduce(..)`, which clearly needs to work left-to-right one at a time; those steps can't be run concurrently and have that operation make any sense. 251 | 252 | As I said, Observables (see [Chapter 10](ch10.md/#observables)) aren't the answer to these kinds of tasks. The reason is, an Observable's coordination of asynchrony is between separate operations, not between steps/iterations at a single level of operation. 253 | 254 | Another way to visualize this distinction is that Observables support "vertical asynchrony", whereas what I'm talking about would be "horizontal asynchrony". 255 | 256 | Consider: 257 | 258 | ```js 259 | var obsv = Rx.Observable.from( [1,2,3,4,5] ); 260 | 261 | obsv 262 | .map( x => x * 2 ) 263 | .delay( 100 ) // <-- vertical asynchrony 264 | .map( x => x + 1 ) 265 | .subscribe( v => console.log ); 266 | // {after 100 ms} 267 | // 3 268 | // 5 269 | // 7 270 | // 9 271 | // 11 272 | ``` 273 | 274 | If for some reason I wanted to ensure that there was a delay of 100 ms between when `1` was processed by the first `map(..)` and when `2` was processed, that would be the "horizontal asynchrony" I'm referring to. There's not really a clean way to model that. 275 | 276 | And of course, I'm using an arbitrary delay in that description, but in practice that would more likely be serial-asynchrony like an asynchronous reduce, where each step in that reduction iteration could take some time before it completes and lets the next step be processed. 277 | 278 | So, how do we support both serial and concurrent iteration across asynchronous operations? 279 | 280 | **fasy** (pronounced like "Tracy" but with an "f") is a little utility library I built for supporting exactly those kinds of tasks. You can find more information about it [here](https://github.com/getify/fasy). 281 | 282 | To illustrate **fasy**, let's consider a concurrent `map(..)` versus a serial `map(..)`: 283 | 284 | ```js 285 | FA.concurrent.map( fetchImage, imageURLs ) 286 | .then( function allImages(imgObjs){ 287 | // .. 288 | } ); 289 | 290 | FA.serial.map( fetchImage, imageURLs ) 291 | .then( function allImages(imgObjs){ 292 | // .. 293 | } ); 294 | ``` 295 | 296 | In both cases, the `then(..)` handler will only be invoked once all the fetches have fully completed. The difference is whether the fetches will all initiate concurrently (aka, "in parallel") or go out one at a time. 297 | 298 | Your instinct might be that concurrent would always be preferable, and while that may be common, it's not always the case. 299 | 300 | For example, what if `fetchImage(..)` maintains a cache of fetched images, and it checks the cache before making the actual network request? What if, in addition to that, the list of `imageURLs` could have duplicates in it? You'd certainly want the first fetch of an image URL to complete (and populate the cache) before doing the check on the duplicate image URL later in the list. 301 | 302 | Again, there will inevitably be cases where concurrent or serial asynchrony will be called for. Asynchronous reductions will always be serial, whereas asynchronous mappings may likely tend to be more concurrent but can also need to be serial in some cases. That's why **fasy** supports all these options. 303 | 304 | Along with Observables, **fasy** will help you extend more FP patterns and principles to your asynchronous operations. 305 | 306 | ## Summary 307 | 308 | JavaScript is not particularly designed as an FP language. However, it does have enough of the basics (like function values, closures, etc.) for us to make it FP-friendly. And the libraries we've examined here will help you do that. 309 | 310 | Armed with the concepts from this book, you're ready to start tackling real-world code. Find a good, comfortable FP library and jump in. Practice, practice, practice! 311 | 312 | So... that's it. I've shared what I have for you, for now. I hereby officially certify you as a "Functional-Light JavaScript" programmer! It's time to close out this "chapter" of our story of learning FP together. But my learning journey still continues; I hope yours does, too! 313 | -------------------------------------------------------------------------------- /manuscript/foreword.md: -------------------------------------------------------------------------------- 1 | # Functional-Light JavaScript 2 | # Foreword 3 | 4 | It's no secret that I am a Functional Programming nut. I evangelize functional ideas and languages wherever I can, try to read the latest academic papers, study abstract algebra in my spare time…the works. Even in JavaScript, I refuse to write an impure statement, which is what led to writing *Professor Frisby's Mostly Adequate Guide to Functional Programming*. Yep, full on, dogmatic zealot. 5 | 6 | I was not always this way… I was once obsessed with objects. I loved modeling the "real world". I was the inventor of synthetic automatons, tinkering through the night with masterful precision. The creator of sentient puppets, fingers dancing on the keyboard to give them life -- a real 1337 h4x0r Geppetto. Yet, after 5 *solid* years of writing object-oriented code, I was never quite satisfied with the outcome. It just never worked out well for me. I felt like a lousy programmer. I even lost faith that a simple, flexible codebase of decent scale was possible. 7 | 8 | I figured I'd try something different: Functional Programming. I began to dabble with functional ideas in my everyday codebase, and much to my coworkers' dismay, hadn't the slightest clue what I was doing. The code I wrote in those days was awful. Atrocious. Digital sewage. The reason was a lack of clear vision or goal on what I was even trying to accomplish. My Jiminy-Coding-Cricket, if you like, was not there to guide me. It took a long time and a lot of garbage programs to figure out how to FP. 9 | 10 | Now, after all that messy exploration, I feel that pure Functional Programming has delivered on its promise. Readable programs do exist! Reuse does exist! I no longer invent, but rather discover my model. I've become a rogue detective uncovering a vast conspiracy, cork board pinned full of mathematical evidence. A digital-age Cousteau logging the characteristics of this bizarre land in the name of science! It's not perfect and I still have a lot to learn, but I've never been more satisfied in my work and pleased with the outcome. 11 | 12 | Had this book existed when I was starting out, my transition into the world of Functional Programming would have been much easier and less destructive. This book is two-fold (right and left): it will not only teach you how to use various constructs from FP effectively in your daily code, but more importantly, provide you with an aim; guiding principles that will keep you on track. 13 | 14 | You will learn Functional-Light: A paradigm that Kyle has pioneered to enable declarative, Functional Programming while providing balance and interop with the rest of the JavaScript world. You will understand the foundation which pure FP is built upon without having to subscribe to the paradigm in its entirety. You will gain the skills to practice and explore FP without having to rewrite existing code for it to work well together. You can take a step forward in your software career without backtracking and wandering aimlessly as I did years ago. Coworkers and colleagues rejoice! 15 | 16 | Kyle is a great teacher known for his relentless pursuit of the whole picture, leaving no nook or cranny unexplored, yet he maintains an empathy for the learner's plight. His style has resonated with the industry, leveling us all up as a whole. His work has a solid place in JavaScript’s history and most people's bookmarks bar. You are in good hands. 17 | 18 | Functional Programming has many different definitions. A Lisp programmer's definition is vastly different from a Haskell perspective. OCaml's FP bears little resemblance to the paradigm seen in Erlang. You will even find several competing definitions in JavaScript. Yet there is a tie that binds -- some blurry know-it-when-I-see-it definition, much like obscenity (indeed, some do find FP obscene!) and this book certainly captures it. The end result might not be considered idiomatic in certain circles, but the knowledge acquired here directly applies to any flavor of FP. 19 | 20 | This book is a terrific place to begin your FP journey. Take it away, Kyle... 21 | 22 | *-Brian Lonsdorf (@drboolean)* 23 | -------------------------------------------------------------------------------- /manuscript/images/fig1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/fig1.png -------------------------------------------------------------------------------- /manuscript/images/fig10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/fig10.png -------------------------------------------------------------------------------- /manuscript/images/fig10.svg: -------------------------------------------------------------------------------- 1 | 2 |
a
a
b
b
c
c
d
d
e
e
predicate function
predicate function
list 1
list 1
list 2
list 2
b
b
d
d
e
e
-------------------------------------------------------------------------------- /manuscript/images/fig11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/fig11.png -------------------------------------------------------------------------------- /manuscript/images/fig11.svg: -------------------------------------------------------------------------------- 1 | 2 |
a
a
b
b
c
c
d
d
e
e
reducer function
reducer function
list 1
list 1
initial
value
[Not supported by viewer]
acc0
[Not supported by viewer]
acc1
[Not supported by viewer]
acc2
[Not supported by viewer]
result
result
acc3
[Not supported by viewer]
-------------------------------------------------------------------------------- /manuscript/images/fig12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/fig12.png -------------------------------------------------------------------------------- /manuscript/images/fig12.svg: -------------------------------------------------------------------------------- 1 | 2 |
a
a
b
b
c
c
d
d
e
e
reducer function
reducer function
list 1
list 1
acc0
[Not supported by viewer]
acc1
[Not supported by viewer]
result
result
acc2
[Not supported by viewer]
initial value
initial value
-------------------------------------------------------------------------------- /manuscript/images/fig13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/fig13.png -------------------------------------------------------------------------------- /manuscript/images/fig13.svg: -------------------------------------------------------------------------------- 1 | 2 |
function foo(x) {
   if (x < 5) return x;
   return foo(x / 2);
}
[Not supported by viewer]
16
[Not supported by viewer]
8
[Not supported by viewer]
function foo(x) {
   if (x < 5) return x;
   return foo(x / 2);
}
[Not supported by viewer]
16
[Not supported by viewer]
8
[Not supported by viewer]
4
[Not supported by viewer]
function foo(x) {
   if (x < 5) return x;
   return foo(x / 2);
}
[Not supported by viewer]
16
[Not supported by viewer]
8
[Not supported by viewer]
4
[Not supported by viewer]
4
[Not supported by viewer]
Step 1
[Not supported by viewer]
Step 2
[Not supported by viewer]
Step 3
[Not supported by viewer]
Step 4
[Not supported by viewer]
function foo(x) {
   if (x < 5) return x;
   return foo(x / 2);
}
[Not supported by viewer]
16
[Not supported by viewer]
-------------------------------------------------------------------------------- /manuscript/images/fig14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/fig14.png -------------------------------------------------------------------------------- /manuscript/images/fig15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/fig15.png -------------------------------------------------------------------------------- /manuscript/images/fig15.svg: -------------------------------------------------------------------------------- 1 | 2 |
baz()

var x;
[Not supported by viewer]
baz()

var x;
[Not supported by viewer]
bar()

var y;
[Not supported by viewer]
baz()

var x;
[Not supported by viewer]
bar()

var y;
[Not supported by viewer]
foo()

var z;
[Not supported by viewer]
Step 1
[Not supported by viewer]
Step 2
[Not supported by viewer]
Step 3
[Not supported by viewer]
-------------------------------------------------------------------------------- /manuscript/images/fig16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/fig16.png -------------------------------------------------------------------------------- /manuscript/images/fig16.svg: -------------------------------------------------------------------------------- 1 | 2 |
baz()

var x;
[Not supported by viewer]
baz()

var x;
[Not supported by viewer]
bar()

var y;
[Not supported by viewer]
baz()

var x;
[Not supported by viewer]
bar()

var y;
[Not supported by viewer]
foo()

var z;
[Not supported by viewer]
Step 1
[Not supported by viewer]
Step 2
[Not supported by viewer]
Step 3
[Not supported by viewer]
-------------------------------------------------------------------------------- /manuscript/images/fig17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/fig17.png -------------------------------------------------------------------------------- /manuscript/images/fig17.svg: -------------------------------------------------------------------------------- 1 | 2 |
Imperative
Imperative
Declarative / FP
Declarative / FP
Readability
Readability
Where many developers give up on learning FP
[Not supported by viewer]
-------------------------------------------------------------------------------- /manuscript/images/fig18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/fig18.png -------------------------------------------------------------------------------- /manuscript/images/fig18.svg: -------------------------------------------------------------------------------- 1 | 2 |
3
3
6
6
1
1
0
0
0: 4
0: 4
3: 1
3: 1
4: 2
4: 2
[4,6,1,1,2]
[4,6,1,1,2]
-------------------------------------------------------------------------------- /manuscript/images/fig19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/fig19.png -------------------------------------------------------------------------------- /manuscript/images/fig19.svg: -------------------------------------------------------------------------------- 1 | 2 |
foo( 16 )
[Not supported by viewer]
foo( 8 )
[Not supported by viewer]
foo( 4 )
[Not supported by viewer]
4
[Not supported by viewer]
4
[Not supported by viewer]
4
[Not supported by viewer]
-------------------------------------------------------------------------------- /manuscript/images/fig2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/fig2.png -------------------------------------------------------------------------------- /manuscript/images/fig3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/fig3.png -------------------------------------------------------------------------------- /manuscript/images/fig3.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /manuscript/images/fig4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/fig4.png -------------------------------------------------------------------------------- /manuscript/images/fig4.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /manuscript/images/fig5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/fig5.png -------------------------------------------------------------------------------- /manuscript/images/fig5.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /manuscript/images/fig6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/fig6.png -------------------------------------------------------------------------------- /manuscript/images/fig6.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /manuscript/images/fig7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/fig7.png -------------------------------------------------------------------------------- /manuscript/images/fig7.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /manuscript/images/fig8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/fig8.png -------------------------------------------------------------------------------- /manuscript/images/fig8.svg: -------------------------------------------------------------------------------- 1 | 2 |
banana
banana
apple
apple
cherry
cherry
apricot
apricot
avocado
avocado
cantaloupe
cantaloupe
cucumber
cucumber
grape
grape
-------------------------------------------------------------------------------- /manuscript/images/fig9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/fig9.png -------------------------------------------------------------------------------- /manuscript/images/fig9.svg: -------------------------------------------------------------------------------- 1 | 2 |
a
a
b
b
c
c
d
d
e
e
V
V
W
W
X
X
Y
Y
Z
Z
mapper function
mapper function
list 1
list 1
list 2
list 2
-------------------------------------------------------------------------------- /manuscript/images/marketing/back-cover-hd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/marketing/back-cover-hd.png -------------------------------------------------------------------------------- /manuscript/images/marketing/front-cover-hd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/marketing/front-cover-hd.png -------------------------------------------------------------------------------- /manuscript/images/marketing/front-cover-hd.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/marketing/front-cover-hd.tiff -------------------------------------------------------------------------------- /manuscript/images/marketing/front-cover-sd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/marketing/front-cover-sd.png -------------------------------------------------------------------------------- /manuscript/images/marketing/front-cover-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/marketing/front-cover-small.png -------------------------------------------------------------------------------- /manuscript/images/marketing/print-book-cover.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/marketing/print-book-cover.pdf -------------------------------------------------------------------------------- /manuscript/images/marketing/print-book-cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/marketing/print-book-cover.png -------------------------------------------------------------------------------- /manuscript/images/marketing/social-avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/marketing/social-avatar.png -------------------------------------------------------------------------------- /manuscript/images/marketing/social-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambda-study-group/Functional-Light-JS/08346500b36091b592c25af4772865b35e05526f/manuscript/images/marketing/social-header.png -------------------------------------------------------------------------------- /manuscript/preface.md: -------------------------------------------------------------------------------- 1 | # Functional-Light JavaScript 2 | # Prefácio 3 | 4 | > *Uma monada é um monoide na categoria dos endofuntores.* 5 | 6 | Você se perdeu ? Não se preocupe, eu também estaria perdido! Todos aqueles termos que significam apenas algo para o já iniciado em Programação Funcional™ (FP) são apenas bobagens confusas para muitos de nós. 7 | 8 | Este livro não vai ensinar o que essas palavras significam. Se é isso que você está procurando, continue procurando. Na verdade, já existem muitos livros ótimos que ensinam o FP * do jeito certo *, de cima para baixo. Essas palavras têm significados importantes e, se você estudar formalmente o PF em profundidade, você vai querer se familiarizar com elas. 9 | 10 | Mas este livro abordará o tópico de maneira bem diferente. Eu vou apresentar conceitos fundamentais de FP a partir do zero, com menos termos especiais ou não-intuitivos do que a maioria das abordagens para FP. Vamos tentar adotar uma abordagem prática para cada princípio em vez de um ângulo puramente acadêmico. ** Haverá termos **, sem dúvida. Mas teremos cuidado e deliberado sobre apresentá-los e explicar por que eles são importantes. 11 | 12 | Infelizmente, eu não sou um membro de carteirinha do FP Cool Kids Club. Eu nunca fui formalmente ensinado sobre FP. E embora eu tenha uma formação acadêmica em CS e eu seja decente em matemática, a notação matemática não é como meu cérebro entende de programação. Eu nunca escrevi uma linha de Scheme, Clojure ou Haskell. Eu não sou um Lisp'r old-school. 13 | 14 | Eu participei de incontáveis ​​conferências sobre FP, cada uma com a desesperada esperança de que finalmente, desta vez, seria o momento em que eu entendi o que é todo esse misticismo de programação funcional. E a cada vez, eu saía frustrado e lembrava que esses termos se confundiam na minha cabeça e eu não tinha ideia se ou o que eu aprendi. Talvez eu tenha aprendido coisas. Mas eu não conseguia descobrir o que essas coisas eram por mais tempo. 15 | 16 | Pouco a pouco, ao longo dessas várias exposições, lancei pedaços de conceitos importantes que parecem vir tão naturalmente ao FPer formal. Aprendi-os lentamente e aprendi de forma pragmática e experimental, não academicamente com terminologia apropriada. Você já conheceu uma coisa por um longo tempo, e só mais tarde descobriu que tinha um nome específico que você nunca conheceu! 17 | 18 | Talvez você seja como eu; Eu ouvi termos como "map-reduce" em torno de segmentos da indústria como "big data" por anos sem nenhuma idéia do que eles eram. Eventualmente eu aprendi o que a função `map (..)` fazia - muito antes de eu ter alguma idéia de que as operações de lista eram a base do caminho do FPer e o que as torna tão importantes. Eu sabia o que * map * era muito antes de eu saber que era chamado `map (..)`. 19 | 20 | Por fim, comecei a reunir todos esses detalhes de compreensão no que agora chamo de "Programação de Luz Funcional" (FLP, Functional-Light Programming). 21 | 22 | ## Missão 23 | 24 | Mas por que é tão importante aprender programação funcional, mesmo a forma leve? 25 | 26 | Eu passei a acreditar em algo muito profundamente nos últimos anos, tanto que você poderia * quase * chamar isso de crença religiosa. Eu acredito que a programação é fundamentalmente sobre humanos, não sobre código. Acredito que o código é, antes de tudo, um meio de comunicação humana, e apenas como um * efeito colateral * (ouve minha risada auto-referencial) instrui o computador. 27 | 28 | Do jeito que eu vejo, a programação funcional é essencial sobre o uso de padrões em seu código que são bem conhecidos, compreensíveis, * e * comprovados para afastar os erros que tornam o código mais difícil de entender. Nessa visão, FP - ou, ahem, FLP! - pode ser uma das coleções mais importantes de ferramentas que qualquer desenvolvedor pode adquirir. 29 | 30 | > A maldição da mônada é que ... uma vez que você entenda ... você perde a capacidade de explicar isso para qualquer outra pessoa. 31 | > 32 | > Douglas Crockford 2012 "Mônadas e gônadas" 33 | > 34 | > https://www.youtube.com/watch?v=dkZFtimgAcM 35 | 36 | Espero que este livro "Talvez" quebre o espírito dessa maldição, mesmo que não falemos sobre "mônadas" até o final nos apêndices. 37 | 38 | O FPer formal freqüentemente afirma que o * valor real * do FP está usando essencialmente 100%: é uma proposta de tudo ou nada. A crença é que, se você usar o FP em uma parte do seu programa, mas não em outro, todo o programa é poluído pelo material não-FP e, portanto, sofre o suficiente para que o FP provavelmente não valha a pena. 39 | 40 | Eu direi inequivocamente: ** Eu acho que o absolutismo é falso **. Isso é tão tolo para mim quanto sugerir que este livro só é bom se eu usar a gramática perfeita e a voz ativa por toda parte; se eu cometer algum erro, isso degrada a qualidade de todo o livro. Absurdo. 41 | 42 | Quanto melhor eu escrever uma voz clara e consistente, melhor será a sua experiência de leitura. Mas eu não sou um autor 100% perfeito. Algumas partes serão melhor escritas que outras. As partes em que eu ainda posso melhorar não invalida as outras partes deste livro que são úteis. 43 | 44 | E assim vai com o nosso código. Quanto mais você puder aplicar esses princípios a mais partes do seu código, melhor será o seu código. Use-os bem 25% do tempo e você terá um bom benefício. Use-os 80% do tempo e você verá ainda mais benefícios. 45 | 46 | Com talvez algumas exceções, não acho que você encontrará muitos absolutos neste texto. Em vez disso, falaremos sobre aspirações, metas, princípios pelos quais lutar. Nós vamos falar sobre equilíbrio e pragmatismo e trade-offs. 47 | 48 | Bem-vindo a esta jornada nos fundamentos mais úteis e práticos do FP. Nós dois temos muito a aprender! 49 | --------------------------------------------------------------------------------