├── .gitignore ├── README.html ├── README.md ├── assets └── images │ ├── array-filter.png │ ├── array-map.png │ ├── array-reduce.png │ ├── map-filter-reduce-in-emoji.png │ ├── map-reduce.png │ ├── refatoracao01.png │ ├── robo.jpg │ └── sanduba.png ├── aulas ├── aula-04 │ ├── exercicio-first-class-RESOLVIDO.js │ ├── exercicio-first-class-jfreitas.js │ ├── exercicio-high-order-01-resolvido.js │ └── exercicio-high-order-02-resolvido.js ├── currying │ ├── currying.js │ ├── operacoes-matematicas.js │ └── sum.js ├── lambda │ └── exemplos-lamdas.md ├── operacoes-matematicas │ └── operacoes-matematicas.md └── slides │ └── JS Funcional FREE - slides - aula 1.pdf ├── capa-email.gif ├── examples.js ├── examples.md ├── examples ├── README.md ├── actions │ ├── filter.funcional.js │ ├── filter.js │ ├── filter.nosso.js │ ├── map.funcional.js │ ├── map.js │ └── map.nosso.js ├── emojis │ └── emojis.js ├── examples.js ├── filter.md ├── map.md ├── reduce.md ├── refactors │ └── README.md ├── subway │ ├── README.md │ └── subway.js └── test │ ├── filter.funcional.spec.js │ ├── filter.nosso.spec.js │ ├── filter.spec.js │ ├── map.funcional.spec.js │ ├── map.nosso.spec.js │ └── map.spec.js ├── images ├── bicho-de-sete-cabecas.jpg ├── function inside function.jpg ├── hadoop-map-reduce.png ├── homer-doh.gif ├── logo-js-funcional.gif ├── logo-webschool-js-funcional.png ├── logo-webschool.png ├── meme-hein.gif ├── meme-jackie-chan.jpg ├── meme-matematica.png ├── meme-pode-isso-arnaldo.jpg ├── meme-realy.jpg ├── meme-shit-happens.jpg ├── meme-talk-is-cheap.jpg ├── meme-wat.jpg ├── meme-yeah-we-will-sse-about-that.jpeg ├── meme_pensando_png_by_mfsyrcm-d5949t0.png └── monad_tutorial.jpg ├── package.json ├── sergin-malan.png └── slides └── images ├── slide-cover-aula-5-parte-2.png ├── slide-cover-aula-5.gif ├── slide-cover-aula-5.png └── slide-cover-aula.psd /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /README.html: -------------------------------------------------------------------------------- 1 | README

Logo da Webschool e do Curso JS Funcional

1018 |

JS Funcional

1019 |

Possuimos 2 grandes paradigmas de programação:

1020 | 1024 |

A Funcional é a mais antiga, sua primeira linguagem foi criada em 1955 (IPL) e posteriomente a mais popular LISP foi criada em 1958. Fortran e COBOL foram criadas respectivamentes em 1956 e 1959, são imperativas.

1025 |

[Explicar como ira funcionar o curso]

1026 |

O que é programação funcional?

1027 |

Programação funcional é um paradigma de programação que trata a computação como uma avaliação de funções matemáticas e evita estados ou dados mutáveis. Utiliza a aplicação de funções, em contraste da programação imperativa, que enfatiza mudanças no estado do programa.

1028 |

Uma função pode ter ou não ter parâmetros e um simples valor de retorno. Os parâmetros são os valores de entrada da função, e o valor de retorno é o resultado da função. A definição de uma função descreve como a função será avaliada em termos de outras funções.

1029 |

Assim como na orientação a objetos a menor parte de um sistema é um objeto, você pode atribuir objetos a variáveis, pode passá-los por parâmetro e ter métodos retornando objetos, na programação funcional, a menor parte do seu sistema é uma função.

1030 |

Por exemplo, a função f(x) = x^2 + 5 é definida utilizando funções de exponenciação e adição. Do mesmo modo, a linguagem deve oferecer funções básicas que não requerem definições adicionais.

1031 |

[ESCREVER MAIS SOBRE]

1032 |

Por que usar programação funcional?

1033 |

Onde usar?

1034 |

Quem está usando?

1035 |

Linguagens funcionais

1036 |

Hoje em dia com o aumento na necessidade de sistemas concorrentes as linguagens funcionais estão voltando para o mercado comercial. Vemos muito grandes empresas usares: Erlang, Haskell, Scala, etc.

1037 |

Linguagens mais conhecidas:

1038 | 1048 |

LISP introduziu a maioria das características hoje encontradas nas modernas linguagens de programação funcional. Scheme foi uma tentativa posterior de simplificar e melhorar LISP. Haskell foi lançada no fim dos anos 1980 numa tentativa de juntar muitas ideias na pesquisa de programação funcional.

1049 |

Lambda

1050 |

O cálculo lambda pode ser considerado a primeira linguagem de programação funcional, embora nunca tenha sido projetada para ser realmente executada em um computador. É um modelo de computação projetado por Alonzo Church nos anos 1930 que oferece um modo muito formal de descrever um cálculo de uma função.

1051 |

A ideia de Church era usar a noção de “processo” ou “transformação” (função) como essencial para fundamentar a matemática, ao invés da noção de conjunto de Cantor. O lambda cálculo não deu muito certo para isso na época, mas acabou sendo importante em outra questão do tempo: a busca pela definição formal do que vem a ser um procedimento efetivo. Em termos atuais, diríamos que essa busca tentava definir formalmente o que é “computação”.

1052 |

(A ideia de usar o conceito de transformação como central na matemática retornou na segunda metade do século XX através da Teoria das Categorias, mas isso é outra história.)

1053 |

Notação

1054 |

Essa notação pode parecer um pouco confusa no início, mas veremos que não é nenhum bicho de sete cabeças.

1055 |

1056 |

Queria ter colocado o Tiamat, mas ele tem 5 cabeças apenas :(

1057 |
(λx.x) y
1058 | 
1059 | 1060 |

Basicamente (λx.x) tem como resultado a expressão y.

1061 |

Onde (E = x, F = y), guardem bem essa informação E é o resultado onde x é substituído pela expressão[função?] F.

1062 |

Agora se F (λx.x)(λx.y) tem como resultado (λx.y), então (E = x, F = (λx.y)). E é só isso, substituição textual.

1063 |

A sintaxe das expressões-lambda é determinada por duas operações: abstração e aplicação (sendo que a aplicação envolve uma operação de substituição chamada conversão-β). Uma expressão-lambda pode ser uma variável, uma abstração de uma expressão, ou uma aplicação de duas expressões:

1064 | 1069 |

A conversão-β é a regra de substituição que diz como a aplicação deve funcionar.

1070 |

Analisemos essa expressão (λ x. + x 1) 4 a conversão-β é:

1071 |
+ 4 1
1072 | 
1073 | 1074 |

Fácil não?

1075 |

meme jackie chan

1076 |

Zuerinha vou explicar é claro, vamos lá:

1077 |
(λ x. + x 1) 4 → + 4 1
1078 | // (λ"variável"."E") "F"
1079 | // variável = x
1080 | // E = + x 1
1081 | // F = 4
1082 | 
1083 | 1084 |

Nesse caso a conversão-β resulta na expressão[?] + 4 1 onde substituímos a variável x da função[?] E pelo valor de F, agora ficou fácil né?

1085 |

meme meme-yeah-we-will-sse-about-that

1086 |

[Falar mais]

1087 |

Teoria das Categorias

1088 |

A teoria das categorias é uma teoria matemática que trata de forma abstrata das estruturas matemáticas e dos relacionamentos entre elas. Ela pode ser entendida como um “jogo de setas”, em que se abstrai o significado das construções.

1089 |

As aplicações da teoria das categorias estendem-se por áreas como álgebra, teoria da recursividade, semântica formal, etc.

1090 |

Uma única operação exigida em uma categoria é a composição. Ouviremos falar muito disso ainda.

1091 | 1097 |

imagem de uma função gigante de matemática apenas porque a zuera não tem limites

1098 |

Functor

1099 |
1100 |

A functor is a function, given a value and a function, unwraps the values to get to its inner value(s), calls the given function with the inner value(s), wraps the returned values in a new structure, and returns the new structure.

1101 |
1102 |

Vamos entender parte por parte:

1103 | 1110 |

meme realy?

1111 |

Sim eu sei que é basicamente a tradução do texto acima, bom então vamos aqo que interessa, códigoooooooooooo:

1112 |
function plus1(value) {
1113 |     return value + 1
1114 | }
1115 | 
1116 | function plus2(value) {
1117 |     return value + 2
1118 | }
1119 | 
1120 | 1121 |

Criamos duas funções simples, plus1 adiciona 1 ao value e plus2 adiciona 2, agora vamos escrever uma função ara que possamos usar qualquer uma dessas funções como e quando necessário:

1122 |

function F(value, fn) {
1123 |     return fn(value)
1124 | }
1125 | 
1126 | F(1, plus1) // 2
1127 | ```js
1128 | 
1129 | Essa função irá funcionar enquanto passarmos um inteiro, vamos tentar passar um *Array*:
1130 | 
1131 | ```js
1132 | F([1, 2, 3], plus1)   ==>> '1,2,31'
1133 | 
1134 | Por exemplo a função `map` é um *functor*!
1135 | 
1136 | No caso do Jasvascript, *filter* é um *functor* porque retorna um *Array*, entretando o *forEach* não é pois retorna *undefined*, ou seja, ele não mantém a estrutura.
1137 | 
1138 | *Functors* são definidos como "[homomorfismos](https://pt.wikipedia.org/wiki/Homomorfismo) entre categorias", vamos entender melhor esse significado:
1139 | 
1140 | - homo = mesmo, igual
1141 | - morfismos = funções que mantém estrutura
1142 | - categoria = tipo
1143 | 
1144 | De acordo com a teoria, a função `F` é um *functor* quando as duas funções comuns combináveis f e g, como no exemplo abaixo:
1145 | 
1146 | 

1147 | F(f . g) = F(f) . F(g)
1148 |

1149 | Onde `.` indicam composição, ou seja, *functors* precisam preservar a composição.
1150 | 
1151 | #####Array Functor
1152 | 
1153 | Como disse que o `map` é um *functor* então vamos provar isso.
1154 | 
1155 | ```js
1156 | function compose(f, g) {
1157 |     return function(x) {return f(g(x))}
1158 | }
1159 | 

1160 |

Fazer composição de funções é criar uma chamada de um conjunto de funções, chamando a função seguinte, com resultados da função anterior. Note que a nossa função de composição acima funciona da direita para a esquerda. g é chamado pela primeira vez, em seguida, f.

1161 |

[Quando mostrar a composição]
1162 | Isso lembra alguma coisa pra você? Bom logo logo verá um exemplo mais conhecido.

1163 |

[ESCREVER MAIS SOBRE]

1164 |

Por que JavaScript é funcional?

1165 |

Funções

1166 |

No JavaScript uma função nada mais é que um objeto que possui atributos como:

1167 | 1172 |

E funções importantes como:

1173 | 1177 |

Para criarmos uma funçõa no JavaScript é muito simples, precisamos apenas utilizar a palavra ´function´:

1178 |

Homer fazendo Doh

1179 |
function add(a, b) {
1180 |   return a + b;
1181 | };
1182 | 
1183 | 1184 |

Podemos usar um exemplo mais simples ainda, uma função que duplica Strings:

1185 |
var repeat = function(s) {
1186 |   return s + s;
1187 | };
1188 | 
1189 | repeat('Na');
1190 | // NaNA
1191 | 
1192 | 1193 |

Então se chamamos apenas a função repeat dessa forma, teremos um resultado indesejado.

1194 |

Meme WAT

1195 |

Sim meu caro aluno, prete atenção no exemplo abaixo:

1196 |
repeat(2);
1197 | // 4
1198 | 
1199 | 1200 |

Aí encontramos o problema!

1201 |

Nesse caso ele não está mais repetindo a String como desejado inicialmente, agora ela está multiplicando o valor por 2 caso seja um Number. Isso porque não temos um contrato com uma função que retorne apenas Strings. Para resolver esse problema é fácil, criamos essa função abaixo:

1202 |
var str = function(s) {
1203 |   if(typeof s !== 'string') {
1204 |     throw new TypeError('Expected a string');
1205 |   }
1206 |   else {
1207 |     return s;
1208 |   }
1209 | }
1210 | 
1211 | 1212 |

Agora você passa uma String para a função, como valor de entrada, e espera-se que seu retorno também seja uma String, como valor de saída.

1213 |

Refatorando nossa função repeat:

1214 |
var repeat = function(s) {
1215 |   var s = str(s)
1216 |   return s + s;
1217 | };
1218 | 
1219 | repeat('Na');
1220 | // NaNA
1221 | repeat(1)
1222 | // TypeError: Expected a string
1223 | 
1224 | 1225 |

[EXPLICAR MAIS]

1226 |

First-class citizens

1227 |

No JavaScript a função é first-class citizen, assim como objeto, entidade ou valor, porque ela suporta todas as operações comuns às outras entidades.

1228 |

Hein!?

1229 |

Essas operações incluem:

1230 | 1235 |

Vamos mostrar cada uma dessas operações com o exemplo anterior:

1236 |
// assinada a uma variável
1237 | var add = function (a, b) {
1238 |   return a + b;
1239 | }
1240 | 
1241 | add(400, 20); // 420
1242 | 
1243 | 1244 |
// retornada de uma função
1245 | function adder(a) {
1246 |   return function(b) {
1247 |     return a + b;
1248 |   }
1249 | }
1250 | 
1251 | var _add =  adder(20);
1252 | _add(400) // 420
1253 | _add(646) // 666
1254 | 
1255 | 1256 |

Podemos melhorar esse exemplo e criarmos a função de multiplicar.

1257 |
// retornada de uma função
1258 | function multiply(a) {
1259 |   var sum = 0;
1260 |   return function(b) {
1261 |     sum = b;
1262 |     for(i=1; i<a; i++){
1263 |       sum += b;
1264 |     }
1265 |     return sum;
1266 |   };
1267 | }
1268 | 
1269 | multiply(2)(333); //666
1270 | 
1271 | 1272 |

Você deve ter percebido que podemos utilizar 2 formas de passagem de parâmetros, correto?

1273 |

Vamos entender melhor como isso funciona, vamos analisar o exemplo coma soma por sem mais simples, porém desta vez vendo os valores dos parâmetros.

1274 |
// retornada de uma função
1275 | function adder(a) {
1276 |   console.log('a', a);
1277 |   return function(b) {
1278 |     console.log('b', b);
1279 |     return a + b;
1280 |   }
1281 | }
1282 | 
1283 | var _add =  adder(20);
1284 | _add(400) // 420
1285 | _add(646) // 666
1286 | 
1287 | 1288 |

Na linha:

1289 |
var _add =  adder(20);
1290 | // a 20
1291 | 
1292 | 1293 |

Basicamente a função está apenas instanciando o valor de a e retornando a função com a soma já usando o a, falaremos mais disso posteriormente, logo _add não recebe o valor de a, mas sim a funçao da soma:

1294 |
function adder(a) {
1295 |   var a = a; // 20
1296 |   return function(b) {
1297 |     return a + b;
1298 |   }
1299 | }
1300 | 
1301 | 1302 |

Depois quando chamamos a função _add passando 400 como parâmetro
1303 |

_add(400)
1304 | // b 420
1305 | // 440
1306 | 

1307 |

Estamos passando o 400 para a função que recebe b desse jeito retornando o valor da nossa soma:

1308 |
function(b) { //400
1309 |   return a + b; //420
1310 | }
1311 | 
1312 | 1313 |

Para entender melhor como isso acontece falarei mais adiante sobre closures.

1314 |

High-order function

1315 | 1319 |

Closures

1320 |

Currying

1321 |

Monads

-------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MUITO ANTIGO NAO LEIAM!! 2 | 3 | # ESSE ANO AINDA TERMINO UM CONTEUDO MUITO MAIS ATUALIZADO E FODA!!!! 4 | 5 | 6 | ###JS Funcional 7 | ![Logo da Webschool e do Curso JS Funcional](https://cldup.com/bn_CJFPZce-2000x2000.png) 8 | 9 | 10 | 11 | 12 | 13 | ### Ementa 14 | * [O Curso](#o-curso) 15 | * [Custo](#custo) 16 | * [Data e Hora](#data-e-hora) 17 | * [O que é programação funcional?](#o-que-é-programação-funcional) 18 | * [Por que usar programação funcional?](#custo) 19 | * [Concorrência](#concorrência) 20 | * [Testes](#testes) 21 | * [Debugging](#debugging) 22 | * [Base teórica](#base-teórica) 23 | * [Onde usar?](#onde-usar) 24 | * [Quem está usando?](#quem-está-usando) 25 | * [Linguagens funcionais](#linguagens-funcionais) 26 | * [Lambda](#lambda) 27 | * [Por que JavaScript é funcional?](#por-que-JavaScript-é-funcional) 28 | * [Funções](#funções) 29 | * [Função anônima](#função-anônima) 30 | * [IIFE](#IIFE) 31 | * [Loops](#loops) 32 | * [First-class Function](#first-class-function) 33 | * [High-order Function](#high-order-function) 34 | * [Closures](#closures) 35 | * [Hoisting](#hoisting) 36 | * [Currying](#currying) 37 | + [Recursion](#recursion) 38 | + [For/list comprehensions](#forlist-comprehensions) 39 | + [Immutability](#immutability) 40 | + [Pure functions](#pure-functions) 41 | + [No side effects](#No-side-effects) 42 | * [Memoization](#memoization) 43 | * [Teoria das Categorias](#teoria-das-ctegorias) 44 | + [Functor](#Functor) 45 | + [Array Functor](#array-Functor) 46 | * [Monads](#monads) 47 | * [Leis da *Monad*](#Leis-da-monad) 48 | * [Pattern matching](#pattern-matching) 49 | * [Tail call](#tail-call) 50 | 51 | 52 | ____ 53 | 54 | **[COMEÇO AULA 1]** 55 | # JS Funcional 56 | 57 | Possuimos 2 grandes paradigmas de programação: 58 | 59 | - Funcional 60 | - Imperativo. 61 | 62 | A Funcional é a mais antiga, sua primeira linguagem foi criada em 1955 (IPL) e posteriomente a mais popular LISP foi criada em 1958. Fortran e COBOL foram criadas respectivamentes em 1956 e 1959, são imperativas. 63 | 64 | O paradigma imperativo é baseada na arquitetura de Von Neumann, enquanto que o funcional é baseado no cálculo lambda. 65 | 66 | ## O Curso 67 | 68 | Esse curso será dividido em módulos, cada um com 4 aulas, 1 por semana de duração máxima de 1 hora e mínima de meia hora. 69 | 70 | Não haverá um dia **FIXO** pois como sou nômade não posso sempre garantir tal data por diversos motivos, logo eu enviarei um email toda semana até Quarta-feira para avisar qual dia a aula ocorrerá sendo entre Quinta e Sábado. 71 | 72 | O turno será o noturno, ainda farei uma pesquisa para ver se preferem as 21 ou 23 horas e as dúvidas deverão ser feitas em um canal, avisarei posteriormente qual, para que eu possa respondê-las e quiçá fazer uma aula avulsa para responder algumas perguntas. 73 | 74 | ### Custo 75 | 76 | > Gratuitamente de graça 77 | 78 | ### Doação 79 | 80 | Caso você seja uma pessoa muito legal e deseja contribuir para esse e outros projetos gratuitos que faço, por exemplo o [JS4Girls](https://github.com/Webschool-io/js4girls), e pela continuidade de mais módulos mensais como: ES6, Node.js, AngularJs, React e outros. 81 | 82 | [Para doar basta clickar no botão do PagSeguro ou do Paypal no site oficial desse curso E GANHE UMA CAMISETA LINDONA!.](http://webschool.io/jsfuncional/camisetas/) Ou mandar email diretamente para webschool.cursos[at]gmail[dot]com pedindo a conta para depósito ou transfêrencia. 83 | ### Local 84 | 85 | **ONLINE** via [https://www.youtube.com/c/webschool-io](https://www.youtube.com/c/webschool-io). 86 | 87 | ### Data e Hora 88 | 89 | Entre Quinta e Sábado sendo ou as 21 ou as 23 horas(se a maioria dos alunos for estudante de faculdade provavelmente preferirá esse), Sábado podendo ser a tarde. 90 | 91 | Cada aula terá uma média de 1 hora, sendo 4 aulas por mês. 92 | 93 | Data e hora podem ser modificados mediante divulgação 12 horas antes. 94 | 95 | Ele acontecerá **AO VIVO** semanalmente, mas caso você não consiga ver ao vivo ele ficará gravado [no nosso canal no Youtube.](https://www.youtube.com/channel/UCKdo1RaF8gzfhvkOdZv_ojg/videos) 96 | 97 | 98 | ## O que é programação funcional? 99 | 100 | Programação funcional é um paradigma de programação que trata a computação como uma avaliação de funções matemáticas e evita estados ou dados mutáveis. Utiliza a aplicação de funções, em contraste da programação imperativa, que enfatiza mudanças no estado do programa. 101 | 102 | Uma função pode ter ou não ter parâmetros e um simples valor de retorno. Os parâmetros são os valores de entrada da função, e o valor de retorno é o resultado da função. A definição de uma função descreve como a função será avaliada em termos de outras funções. 103 | 104 | Assim como na orientação a objetos a menor parte de um sistema é um objeto, você pode atribuir objetos a variáveis, pode passá-los por parâmetro e ter métodos retornando objetos, na programação funcional, a menor parte do seu sistema é uma função. 105 | 106 | Por exemplo, a função f(x) = x^2 + 5 é definida utilizando funções de potência e adição. Do mesmo modo, a linguagem deve oferecer funções básicas que não requerem definições adicionais. 107 | 108 | Vamos conhecer alguns fundamentos: 109 | 110 | - não existe conceito de variáveis ou atribuição 111 | - iterações devem ser construídas com recursão 112 | 113 | 114 | [ESCREVER MAIS SOBRE] 115 | 116 | ### Por que usar programação funcional? 117 | 118 | Temos 4 grandes motivos para usar programação funcional, são eles: 119 | 120 | - Concorrência: não temos deadlocks ou race conditions simplesmente porque não precisamos de locks - o dado é imutável; 121 | - Testes: criar testes unitários sem se preocupar com o estado simplesmente porque não existe estado. Devemos preocupar apenas com os argumentos das funções que nós testamos; 122 | - Debugging: rastrear algum valor no stack trace é bem simples; 123 | - Base teórica: linguagens funcionais são baseados no cálculo lambda, que é um sistema formal. Esta fundamentação teórica faz a prova para correção dos programas seja muito simples (por exemplo, usando indução). 124 | 125 | #### Concorrência 126 | 127 | Os processadores multicore estão presentes em praticamente todos os computadores modernos, inclusive em dispositivos móveis como telefones celular e tablets. Porém, pouco desse poder de processamento, provido pelos múltiplos núcleos, é aproveitado de maneira efetiva pelas aplicações devido à dificuldade de se escrever sistemas concorrentes. 128 | 129 | Com o objetivo de tornar o desenvolvimento desse tipo de sistema mais palpável, alguns novos mecanismos de sincronização e paralelismo vem sendo propostos em linguagens de programação funcional. Esse tipo de linguagem prega um estilo de programação baseado em funções puras e dados imutáveis que facilita o desenvolvimento de programas concorrentes. 130 | 131 | A concorrência nas linguagens imperativas tradicionais é relativamente complexa, o programador é o responsável pela sincronização de todas as tarefas. 132 | 133 | Entretanto, as linguagens funcionais nos oferece oportunidades para a concorrência: 134 | 135 | - A partir do momento em que uma função tem mais de um parâmetro, estes parâmetros devem em princípio ser avaliados simultaneamente (note que os parâmetros seriam as funções correspondentes às tarefas a serem executadas); 136 | - A partir deste ponto, a responsabilidade pela sincronização das tarefas passa do programador para o compilador. 137 | 138 | Se você pensar que um dos pilares da programação funcional é compor várias funç~oes de uma só vez, nesse caso fazendo-as serem processadas em paralelo. 139 | 140 | Todavia, as linguagens funcionais orientadas a multitarefa permitem ao programador trabalhar em um nível muito mais elevado do que as linguagens imperativas destinadas a este mesmo fim. 141 | 142 | [Confirmar dados sobre WebWorker em JS p/ concorrência] 143 | 144 | #### Testes 145 | 146 | Com funções puras os testes ficam muito mais fáceis, não precisamos *mockar* um meio de pagamento ou testes de estado em cada teste. Nós simplesmente passamos uma entrada e validamos uma saída. 147 | 148 | #### Debugging 149 | #### Base teórica 150 | 151 | #### Onde usar? 152 | 153 | BI, Sistemas concorrentes. 154 | 155 | ![Diagrama de Map e REduce do Hadoop](https://cldup.com/GxuJ2yHNnS-1200x1200.png) 156 | 157 | #### Quem está usando? 158 | 159 | Spark, Netflix, Google, Facebook, [Amazon (Amazon Lambda)](http://www.infoworld.com/article/2847466/amazon-web-services/amazon-lambda-bridges-functional-programming-and-cloud.html), sistemas de avião como da família Airbus A340. 160 | 161 | ### Linguagens funcionais 162 | 163 | Hoje em dia com o aumento na necessidade de sistemas concorrentes as linguagens funcionais estão voltando para o mercado comercial. Vemos muito grandes empresas usarem: Erlang, Haskell, Scala, etc. 164 | 165 | Linguagens mais conhecidas: 166 | 167 | - Erlang; 168 | - F#; 169 | - Haskell; 170 | - Lisp; 171 | - OCaml; 172 | - R; 173 | - Scala; 174 | - Scheme. 175 | 176 | LISP introduziu a maioria das características hoje encontradas nas modernas linguagens de programação funcional. Scheme foi uma tentativa posterior de simplificar e melhorar LISP. Haskell foi lançada no fim dos anos 1980 numa tentativa de juntar muitas ideias na pesquisa de programação funcional. 177 | 178 | 179 | ##### Erlang 180 | 181 | Além da Ericsson, é lógico, há algumas outras grandes empresas e projetos usando Erlang, como por exemplo: 182 | 183 | - Facebook, no backend de seu sistema de chat, lidando com 100 milhões de usuários ativos; 184 | - Delicious, que tem mais de 5 milhões de usuários e mais de 150 milhões de bookmarks; 185 | - Amazon SimpleDB, o serviço de dados do poderoso Amazon EC2; 186 | - GitHub, no seu sistema de backend, lidando com milhares de transações concorrentes; 187 | - Whatsapp, utiliza a linguagem no backend de seus aplicativos; 188 | - Motorola; 189 | - CouchDB; 190 | - RabbitMQ. 191 | Dados retirados daqui: , 193 | 194 | ##### Elixir 195 | 196 | Como a sintaxe de Erlang pode não ser convidativa para desenvolvedores "modernos", por isso José Valim desenvolveu o Elixir, linguagem com sintaxe moderna que roda dentro da madura VM do Erlang. 197 | 198 | Atualmente na versão 1.0, com sua framework web(Phoenix) dando os primeiros passos. 199 | 200 | Saiba mais em: 201 | Elixir: 202 | Phoenix: 203 | 204 | ### Lambda 205 | O cálculo lambda pode ser considerado a primeira linguagem de programação funcional, embora nunca tenha sido projetada para ser realmente executada em um computador. É um modelo de computação projetado por [Alonzo Church](https://pt.wikipedia.org/wiki/Alonzo_Church) nos anos 1930 que oferece um modo muito formal de descrever um cálculo de uma função. 206 | 207 | A ideia de Church era usar a noção de “processo” ou “transformação” (função) como essencial para fundamentar a matemática, ao invés da noção de [conjunto de Cantor](https://pt.wikipedia.org/wiki/Conjunto_de_Cantor). O cálculo lambda não deu muito certo para isso na época, mas acabou sendo importante em outra questão do tempo: a busca pela definição formal do que vem a ser um procedimento efetivo. Em termos atuais, diríamos que essa busca tentava definir formalmente o que é “computação”. 208 | 209 | (A ideia de usar o conceito de transformação como central na matemática retornou na segunda metade do século XX através da Teoria das Categorias, mas isso é outra história.) 210 | 211 | #### Notação 212 | 213 | Essa notação pode parecer um pouco confusa no início, mas veremos que não é nenhum bicho de sete cabeças. 214 | 215 | ![bicho de sete cabeças](https://cldup.com/gov3Q0J67O-1200x1200.jpeg) 216 | 217 | *Queria ter colocado o Tiamat, mas ele tem 5 cabeças apenas :(* 218 | 219 | Normalmente em uma linguagem tipada e imperativa nós faríamos isso: 220 | 221 | ```c 222 | int x; 223 | 224 | int f(int x) { 225 | return x; 226 | } 227 | ``` 228 | 229 | Porém quando começamos a trabalhar com linguagens funcionais acabamos vendo uma outra forma de se escrever a mesma coisa. 230 | 231 | ``` 232 | x: int 233 | f: int -> int 234 | _______________ 235 | x: a 236 | f: a -> a 237 | ``` 238 | 239 | Ai você se pergunta: 240 | 241 | - *Mas por que assim?* 242 | 243 | Lembra que falei que toda a base é matemática? Então. 244 | 245 | O tipo de uma variável é o conjunto de valores em que ele está limitado, na matemática falamos que: 246 | 247 | ``` 248 | x E Z 249 | ``` 250 | 251 | > x pertence ao conjunto dos inteiros 252 | > função f recebe um inteiro e retorna um inteiro. 253 | 254 | ps: se fosse apenas os inteiros positivos falaríamos que pertence aos naturais. 255 | 256 | Logo dessa forma fica bem mais "matemático" de se ler. No exemplo em que usamos o `a`, ele significa **qualquer** tipo. 257 | 258 | Vamos ver como fica a notação de composição. 259 | 260 | ``` 261 | x: a 262 | f: a -> a 263 | g: a -> a 264 | ``` 265 | 266 | Para começar vamos chamar essas duas funções juntas: 267 | 268 | ```js 269 | f(g(a)) 270 | ``` 271 | 272 | Nesse caso estamos dizendo que quero executar a função `g` com o argumento `a` e sua resposta será passada para a chamada da função `f`. 273 | 274 | Agora usando a notação de composição ficará: 275 | 276 | ``` 277 | (f . g) = h 278 | ``` 279 | 280 | Agora `h` é uma nova função, que tem qual tipo? O tipo que definimos em `g` que é `a`. 281 | 282 | ``` 283 | (f . g) = h : a -> a 284 | ``` 285 | 286 | Logo a função `h` precisa receber um valor do tipo `a` e retornar um valor do tipo `a`. 287 | 288 | #### Expressão Lambda 289 | 290 | Agora vamos ver como é uma expressão lambda: 291 | 292 | ```haskell 293 | (λx.x) 294 | ``` 295 | 296 | Convertendo isso em Javascript para entendermos melhor fica: 297 | 298 | ```js 299 | function(x) { 300 | return x; 301 | } 302 | ``` 303 | 304 | Porém isso aqui não é uma expressão lambda: 305 | 306 | 307 | ```haskell 308 | (λy.x) 309 | ``` 310 | 311 | Porque o `x` é uma variável livre, ele não está ligado à função `λy`, ficando assim em JavaScript: 312 | 313 | ```js 314 | function(y) { 315 | return x; 316 | } 317 | ``` 318 | 319 | Para usarmos o `y` corretamente faremos o seguinte: 320 | 321 | ```haskell 322 | (λx.(λy.x)) 323 | ``` 324 | 325 | O sparentêsis não são necessários, coloquei apenas para demonstrar que o `x` é livre na expressão interior `(λy.x)` e ligada na expressão externa, ficando assim: 326 | 327 | 328 | ```haskell 329 | λx.λy.x 330 | 331 | ``` 332 | 333 | Fica em JavaScript: 334 | 335 | ```js 336 | function(x) { 337 | return function(y) { 338 | return x; 339 | } 340 | } 341 | ``` 342 | 343 | Vamos ver agora como fica uma expressão lambda, onde teremos agora qual o valor que será passado para a expressão. 344 | 345 | ```haskell 346 | (λx.x) y 347 | ``` 348 | Basicamente `(λx.x)` tem como resultado a expressão avaliada passando o `y` como valor. 349 | 350 | Também podemos ver uma expressão lambda dessa maneira: 351 | 352 | 353 | ```haskell 354 | (λx.E) F 355 | ``` 356 | 357 | Onde `(E = x e F = y)`, guardem bem essa informação `E` é o resultado onde `x` é substituído pela expressão `F`. 358 | 359 | 360 | #### Aplicação Lambda 361 | 362 | ```haskell 363 | λx.x UNICORNIO -> UNICORNIO 364 | ``` 365 | 366 | 367 | ```js 368 | function(x) { 369 | return x; 370 | }(UNICORN); 371 | ``` 372 | 373 | 374 | ```haskell 375 | λx.λy.x UNICORNIO -> λy.UNICORNIO 376 | ``` 377 | 378 | Essa função recebe um arguento e retorna uma função que retorna o mesmo valor. 379 | 380 | **EXERCICIO** 381 | 382 | Escrever essa função `λx.λy.x UNICORNIO -> λy.UNICORNIO` em JavaScript. 383 | 384 | 385 | **Não está nos slides** 386 | ##### Self-application 387 | ```haskell 388 | λs.(s s) -> UNICORNIO 389 | ``` 390 | 391 | ```js 392 | function(f) { 393 | return f(f); 394 | } 395 | ``` 396 | 397 | A sintaxe das expressões-lambda é determinada por duas operações: abstração e aplicação (sendo que a aplicação envolve uma operação de substituição chamada conversão-β). Uma expressão-lambda pode ser uma variável, uma abstração de uma expressão, ou uma aplicação de duas expressões: 398 | 399 | - Variáveis: x, y, z, um conjunto qualquer de nomes de variáveis; 400 | - Abstrações: dada uma expressão-lambda E, podemos formar uma abstração de E usando `λ + variável + ‘.’ + E`. Por exemplo: `λx.x`; 401 | - Aplicações: dadas duas expressões-lambda `E` e `F`, a expressão da aplicação é formada pela justaposição de uma ao lado da outra: E F. 402 | 403 | **[MELHORAR COM EXEMPLOS SIMPLES]** 404 | 405 | A conversão-β é a regra de substituição que diz como a aplicação deve funcionar. 406 | 407 | Analisemos essa expressão `(λ x. + x 1) 4` a conversão-β é: 408 | 409 | ``` 410 | + 4 1 411 | ``` 412 | 413 | **Fácil não?** 414 | 415 | ![meme jackie chan](https://cldup.com/EZEjcYdurI-1200x1200.jpeg) 416 | 417 | Zuerinha vou explicar é claro, vamos lá: 418 | 419 | ```haskell 420 | (λ x. + x 1) 4 → + 4 1 421 | // (λ"variável"."E") "F" 422 | // variável = x 423 | // E = + x 1 424 | // F = 4 425 | ``` 426 | 427 | Nesse caso a conversão-β resulta na expressão `+ 4 1` onde substituímos a variável `x` da expressão `E` pelo valor de `F`, agora ficou fácil né? 428 | 429 | ![meme meme-yeah-we-will-sse-about-that](https://cldup.com/9dhGMxmsQW-1200x1200.jpeg) 430 | 431 | 432 | [Falar mais] 433 | 434 | Nós usaremos esse tipo de notação apenas para exemplos, pois o Javascript não possui essa sintaxe. 435 | 436 | 437 | ### Por que JavaScript é funcional? 438 | 439 | ## Funções 440 | 441 | No JavaScript uma função nada mais é que um objeto que possui atributos como: 442 | 443 | - arguments 444 | - name 445 | - length 446 | 447 | E funções importantes como: 448 | 449 | - apply 450 | - bind 451 | - call 452 | 453 | Para criarmos uma função no JavaScript é muito simples, como já vimos anteriormente, precisamos apenas utilizar a palavra `function`. 454 | 455 | ![Homer fazendo Doh](https://cldup.com/CVvUx6Uswo.gif) 456 | 457 | Estava vendo [essa palestra](http://www.infoq.com/br/presentations/programacao-funcional-por-que-importa) aqui hoje e percebi que o jeito mais fácil de entender programação funcional é algo que sempre falei e sempre tentei seguir: 458 | 459 | >TODA FUNÇÃO PRECISA RETORNAR UM VALOR! 460 | 461 | Sabendo dessa premissa como faríamos um simples atribuição de valor como: 462 | 463 | ```js 464 | var idade = 30; 465 | ``` 466 | Simples, assim: 467 | 468 | ```js 469 | function setIdade(idade) { return idade; } 470 | 471 | ``` 472 | 473 | Nesse caso isso é uma função identidade. 474 | 475 | Logo eu posso testar se é maior de idade assim: 476 | 477 | ```js 478 | function maioridade(idade) { 479 | return idade >= 18; 480 | } 481 | ``` 482 | 483 | E chamamos ela da seguinte forma: 484 | 485 | ```js 486 | maioridade(setIdade(30)); 487 | ``` 488 | 489 | Bem fácil né??? 490 | 491 | Dessa forma podemos pensar que ele se assemelha muito ao *Atomic Design* onde criamos pequenos átomos independentes, nesse caso as funções e com elas vamos compondo funções maiores, **exatamente** como visto [aqui no artigo do Brad Frost](http://bradfrost.com/blog/post/atomic-web-design/). 492 | 493 | Agora vamos usar um exemplo mais simples ainda, uma função que duplica *Strings*: 494 | 495 | ```js 496 | var repeat = function(s) { 497 | return s + s; 498 | }; 499 | 500 | repeat('Na'); 501 | // NaNa 502 | ``` 503 | 504 | Então se chamamos apenas a função `repeat` dessa forma, passando *String* então estará correta, porém se não fizermos isso teremos um resultado indesejado. 505 | 506 | ![Meme WAT](https://cldup.com/BOagKEB49C.gif) 507 | 508 | Sim meu caro aluno, prete atenção no exemplo abaixo: 509 | 510 | ```js 511 | repeat(2); 512 | // 4 513 | ``` 514 | 515 | Aí encontramos o problema! 516 | 517 | Nesse caso ele não está mais repetindo a *String* como desejado inicialmente, agora ela está multiplicando o valor por 2 caso seja um *Number*. Isso porque não temos um contrato com uma função que retorne apenas *Strings*. Para resolver esse problema é fácil, criamos essa função abaixo: 518 | 519 | ```js 520 | var str = function(s) { 521 | if(typeof s !== 'string') { 522 | throw new TypeError('Expected a string'); 523 | } 524 | return s; 525 | } 526 | ``` 527 | 528 | Agora você passa uma *String* para a função, como valor de entrada, e espera-se que seu retorno também seja uma *String*, como valor de saída. 529 | 530 | Refatorando nossa função `repeat`: 531 | 532 | ```js 533 | var repeat = function(s) { 534 | var s = str(s) 535 | return s + s; 536 | }; 537 | 538 | repeat('Na'); 539 | // NaNa 540 | repeat(1) 541 | // TypeError: Expected a string 542 | ``` 543 | 544 | 545 | #### Função anônima 546 | 547 | Como vimos na parte de lambda a função anônima é apenas a chamada do `function` sem nomeá-la, sendo o lambda uma função anônima que aceita apenas um parâmetro. 548 | 549 | ```js 550 | function(nome) { 551 | return "Ola " + nome; 552 | }; 553 | ``` 554 | 555 | Mas ai você deve se perguntar: 556 | 557 | **\-Tá mas como eu faço para rodar essa função?** 558 | 559 | Então uma função anônima serve para que você passe uma função como parâmetro sem precisar guardar ela em uma variável, como nesse exemplo abaixo: 560 | 561 | ```js 562 | $.ajax('http://cors-server.getup.io/url/api.redtube.com/?data=redtube.Videos.searchVideos&search=Sasha%20Gray') 563 | .done(function(data) {console.log(data)}); 564 | ``` 565 | **É LARGAMENTE UTILIZADO NO JAVASCRIPT!** 566 | 567 | #### IIFE - Immediately-Invoked Function Expression 568 | 569 | No JavaScript cada vez que você executa uma função você cria um escopo fechado para ela de onde as variáveis não podem "sair", tendo assim um mecanismo simples de privacidade. 570 | 571 | ```js 572 | function makeCounter() { 573 | var i = 0; 574 | 575 | return function() { 576 | console.log( ++i ); 577 | }; 578 | } 579 | 580 | // Note que `counter` e `counter2` cada uma tem um escopo separado para `i`. 581 | 582 | var counter = makeCounter(); 583 | counter(); // 1 584 | counter(); // 2 585 | 586 | var counter2 = makeCounter(); 587 | counter2(); // 1 588 | counter2(); // 2 589 | 590 | i; // ReferenceError: i is not defined (só existe dentro de makeCounter) 591 | ``` 592 | 593 | Bom se para executarmos uma função basta colocar `()` após o nome da função, podemos então substituir o noe da função pela própria declaração dela. 594 | 595 | Hein? 596 | 597 | Bom fica mais fácil olhando o código a seguir: 598 | 599 | ```js 600 | function(){ /* code */ }(); // SyntaxError: Unexpected token ( 601 | ``` 602 | 603 | O problema aqui é que quando o interpretador do javaScript encontra a palavra `function` ele trata como uma declaração de função, para resolver esse problemas nós só precisamos encapsular ela entre `()`. 604 | 605 | 606 | ```js 607 | (function(){ /* code */ }()); 608 | (function(){ /* code */ })(); 609 | 610 | ``` 611 | 612 | Uma outra forma de ver isso é assim: 613 | 614 | ```js 615 | function foo() { foo(); } 616 | ``` 617 | 618 | Já para eu executar uma função anônima eu faço: 619 | 620 | ```js 621 | var foo = function() { arguments.callee(); }; 622 | ``` 623 | 624 | ### First-class Functions 625 | 626 | No JavaScript a função é first-class citizen, assim como objeto, entidade ou valor, porque ela suporta todas as operações comuns às outras entidades. 627 | 628 | ![Hein!?](https://cldup.com/Oul_G5l7FB.gif) 629 | 630 | Essas operações incluem: 631 | 632 | - assinada a uma variável 633 | - retornada de uma função 634 | - ser passada por parâmetro 635 | 636 | Vamos mostrar cada uma dessas operações com o exemplo anterior: 637 | 638 | ```js 639 | // assinada a uma variável 640 | var add = function (a, b) { 641 | return a + b; 642 | } 643 | 644 | add(400, 20); // 420 645 | ``` 646 | 647 | ```js 648 | // retornada de uma função 649 | function adder(a) { 650 | return function(b) { 651 | return a + b; 652 | } 653 | } 654 | 655 | var _add = adder(20); 656 | _add(400) // 420 657 | _add(646) // 666 658 | ``` 659 | 660 | Podemos melhorar esse exemplo e criarmos a função de multiplicar. 661 | 662 | ```js 663 | // retornada de uma função 664 | function multiply(a) { 665 | var sum = 0; 666 | return function(b) { 667 | sum = b; 668 | for(i=1; i v*2); 995 | console.log(x) //[2,4,6] 996 | ``` 997 | 998 | ####Filter 999 | Essa já é um pouco mais simples de entender, ela vai criar um **teste lógico** dentro de um **callback**, e a partir disso devolver um **array** com os valores do **outro array** que passaram no teste! Exemplo: 1000 | 1001 | ```javascript 1002 | var x = [1,2,3].filter(function (val) { 1003 | return val % 2 == 0 1004 | }); 1005 | 1006 | console.log(x); //[2] 1007 | ``` 1008 | Em ES6: 1009 | 1010 | ```javascript 1011 | var x = [1,2,3].filter(v => v % 2 == 0); 1012 | console.log(x); //[2] 1013 | ``` 1014 | 1015 | ####Reduce / ReduceRight 1016 | Reduce é uma função incrível! Esse método realiza um **callback**! Sua função? **Reduzir um array à um único valor!** Como faz isso? Aplica um **callback** para um acumulador a partir de cada valor em um array. O **Reduce** faz isso da **Esquerda > Direita**, o **ReduceRight** faz a mesma coisa porem da **Direita > Esquerda**. Ja que esse é um pouco mais complicado, vamos dar uma olhada na estrutura alem do exemplo: 1017 | 1018 | ```javascript 1019 | //array.reduce(function(acumulador, valor , index, array ) {...} 1020 | 1021 | //Em ação! 1022 | var x = [1,2,3].reduce(function(a, v, i ,arr) { 1023 | return a + v; 1024 | }); 1025 | console.log(x) //6 1026 | ``` 1027 | 1028 | Em ES6: 1029 | ```javascript 1030 | var x = [1,2,3].reduce((a, v, i ,arr) => a + v); 1031 | console.log(x) //6 1032 | ``` 1033 | 1034 | ####Every/Some 1035 | Esses métodos como alguns outros são esquecido por muitos, porem, podem ser **muito úteis**! Tanto o **every** quanto o **some** são **testes de array**. 1036 | 1037 | O **every** age como o fator **AND**, ou seja, se **TODOS** os elementos passarem no teste, ele retornara **true**, porem, basta apenas **um** não cumprir a regra para restornar um false. 1038 | 1039 | O **some** age como o fator **OR**, em outras palavras, se apenas **UM** elemento cumprir a regra ele retornara **true**. Em exemplo: 1040 | 1041 | ```javascript 1042 | var x = [1,2,3].every(function(v){ 1043 | return v+v >= 2 1044 | }) 1045 | console.log(x) //true 1046 | 1047 | //VENDO A DIFERENÇA 1048 | var y = [1,2,3].every(function(v){ 1049 | return v*v >= 9 1050 | }) 1051 | console.log(y) //false 1052 | 1053 | var y = [1,2,3].some(function(v){ 1054 | return v*v >= 9 1055 | }) 1056 | console.log(y) //true 1057 | ``` 1058 | 1059 | Em ES6: 1060 | ```javascript 1061 | var x = [1,2,3].every(v => v+v >= 2) 1062 | console.log(x) //true 1063 | ``` 1064 | 1065 | #### Agora na prática! 1066 | 1067 | Imaginemos que tenho que pegar a **média** dos **maiores de idade** que frequentam um **clube**. 1068 | 1069 | **Simulando um retorno de Banco** 1070 | ```javascript 1071 | var pessoas = [ 1072 | {name : "Guilherme", age: 21}, 1073 | {name : "Mario", age: 16}, 1074 | {name : "Luiza", age: 17}, 1075 | {name : "Carlos", age: 19}, 1076 | {name : "André", age: 26}, 1077 | {name : "Bianca", age: 18}, 1078 | {name : "Maria", age: 14} 1079 | ] 1080 | ``` 1081 | 1082 | **Como fariamos isso sem ArrayAPI** 1083 | ```javascript 1084 | var maioresDe18 = []; 1085 | for (var i = 0; i < pessoas.length; i++) { 1086 | if (pessoas[i].age >= 18) maioresDe18.push(pessoas[i]) 1087 | } 1088 | 1089 | var mediaIdade = 0; 1090 | for (i = 0; i < maioresDe18.length; i++){ 1091 | mediaIdade += maioresDe18[i].age 1092 | } 1093 | 1094 | mediaIdade = mediaIdade/maioresDe18.length; 1095 | console.log(mediaIdade) 1096 | ``` 1097 | 1098 | **Com Array API** 1099 | ```javascript 1100 | var mediaIdade = pessoas.filter(function (p) { 1101 | return p.age >= 18 1102 | }).map(function(p) { 1103 | return p.age 1104 | }).reduce(function(a, v, i, arr) { 1105 | return i + 1 == arr.length ? (a + v) / arr.length : a + v 1106 | }) 1107 | ``` 1108 | 1109 | **Com uma firulas e ES6** 1110 | ```javascript 1111 | var media = (a, v, i, arr) => i + 1 == arr.length ? (a + v) / arr.length : a + v, 1112 | maioridade = p => p.age >= 18, 1113 | mediaIdade = pessoas 1114 | .filter(maioridade) //Retorna maiores de 18 1115 | .map(p => p.age) //Transforma objetos apenas em idades 1116 | .reduce(media); // Faz media 1117 | 1118 | console.log(mediaIdade); //21 1119 | ``` 1120 | 1121 | #### Menção Honrosa (.forEach) 1122 | As vezes não queremos um retorno! Só queremos um **for** facilitado! Por mais que não goste, existe o **.forEach** no ArrayAPI que permite que você emule o famoso **for (int x : list)** do java e outras linguagens! 1123 | ```javascript 1124 | [1,2,3].forEach(function(n) { 1125 | console.log("Estamos vendo o numero: "+n); 1126 | }) 1127 | ``` 1128 | 1129 | ### Hoisting 1130 | 1131 | Em uma tradução mais literal *hoisting* significa: içar, levantar. É uma característica do JavaScript que nao temos como fugir, mas no ES6 isso ja muda com `let`. 1132 | 1133 | Aí você deve se perguntar: 1134 | 1135 | **-Mas o que que ele vai içar?** 1136 | 1137 | Pois eu lhe respondo: 1138 | 1139 | **-As declarações de variáveis.** 1140 | 1141 | ![](http://m.memegen.com/l81732.jpg) 1142 | 1143 | > Because variable declarations (and declarations in general) are processed before any code is executed, declaring a variable anywhere in the code is equivalent to declaring it at the top. This also means that a variable can appear to be used before it's declared. This behavior is called "hoisting", as it appears that the variable declaration is moved to the top of the function or global code. 1144 | 1145 | ```js 1146 | bla = 2 1147 | var bla; 1148 | 1149 | // implicitamente é interpretado como: 1150 | 1151 | var bla; 1152 | bla = 2; 1153 | ``` 1154 | 1155 | fonte: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var) 1156 | 1157 | Todos os artigos e vídeos sobre *Hoisting* **QUASE SEMPRE** começam com esse exemplo: 1158 | 1159 | ```js 1160 | // Exemplo 1 1161 | var a = 1 1162 | 1163 | function foo() { 1164 | console.log(a) 1165 | var a = 2 1166 | console.log(a) 1167 | } 1168 | 1169 | foo() 1170 | 1171 | console.log(a) 1172 | ``` 1173 | 1174 | Então eu não fazer isso, já que é muito fácil você achar a explicação desse código simples acima, vou até indicar esse vídeo maravilhoso que vi dia desses [Dicas Javascript - Ep01: Hoisting](https://www.youtube.com/watch?v=JGpekHQ_9kY) 1175 | 1176 | Vou explicar do mais básico ainda! 1177 | 1178 | ![](http://www.quickmeme.com/img/8b/8b176b52163a66ce75d1b6423b6b510b0830653bb028ccf09f78fb15d25dfe93.jpg) 1179 | 1180 | Vamos analisar o seguinte código: 1181 | 1182 | ```js 1183 | var idade = 30; 1184 | 1185 | if(idade === 666){ 1186 | var paiinho = "Belzebu"; 1187 | } 1188 | 1189 | console.log(idade); // 30 1190 | console.log(paiinho); // undefined 1191 | console.log(maiinha); // Uncaught ReferenceError: maiinha is not defined(…) 1192 | ``` 1193 | 1194 | Por que você acha que o valor de `idade` é `30`, do `paainho` é `undefined` e `maiinha` é um **erro**? 1195 | 1196 | Bom o primeiro é facil né? O valor de `idade` é `30` pois definimos esse valor na primeira linha e nao o modificamos mais. Porém o valor de `paainho` é `undefined` e nao `Belzebu` como "deveria" ser e é aqui que esta acontecendo o *hoisting*, veja nesse código abaixo como fica o código explícito: 1197 | 1198 | ```js 1199 | var idade = 30; 1200 | var paiinho = undefined; 1201 | 1202 | if(idade === 666){ 1203 | paiinho = "Belzebu"; 1204 | } 1205 | 1206 | console.log(idade); // 30 1207 | console.log(paiinho); // undefined 1208 | console.log(maiinha); // Uncaught ReferenceError: maiinha is not defined(…) 1209 | ``` 1210 | 1211 | Percebeu o porquê `paiinho` nao teve seu valor alterdo para `Belzebu`? 1212 | 1213 | Porque o código nunca entrou no `if`, por isso ele permaneceu com seu valor inicial que era `undefined`. 1214 | 1215 | 1216 | 1217 | Malandro pra caraleo esse esquea né? Pois é tome muito cuidado com suas variaveis elas podem estar com valores diferentes do que você imagina. 1218 | 1219 | Porém assim ainda esta simples de perceber, a coisa começa a piorar quando colocamos funções na jogada, vamos analisar o seguinte código: 1220 | 1221 | ```js 1222 | var idade = 30; 1223 | 1224 | function setPaiinho() { 1225 | var paiinho = "Belzebu"; 1226 | } 1227 | 1228 | console.log(idade); // 30 1229 | console.log(paiinho); // undefined 1230 | ``` 1231 | 1232 | 1233 | 1234 | # Explicar mais sobre hoisting!!!! 1235 | 1236 | 1237 | 1238 | 1239 | 1240 | ### Currying 1241 | 1242 | 1243 | #### Pense NUm treco foda 1244 | 1245 | ``` 1246 | function curry(fn) { 1247 | var c = curry.bind(this, fn = fn.bind.apply(fn, [this].concat([].slice.call(arguments, 1)))); 1248 | 1249 | c.exec = fn; 1250 | 1251 | return c; 1252 | } 1253 | 1254 | function sum() { 1255 | return [].reduce.call(arguments, function (c, n) { 1256 | return c + n; 1257 | }); 1258 | } 1259 | 1260 | console.log(curry(sum, 1, 2)(3)(4, 5)(6, 7, 8).exec()); 1261 | ``` 1262 | 1263 | ### Teoria das Categorias 1264 | 1265 | A teoria das categorias é uma teoria matemática que trata de forma abstrata das estruturas matemáticas e dos relacionamentos entre elas. Ela pode ser entendida como um "jogo de setas", em que se abstrai o significado das construções. 1266 | 1267 | As aplicações da teoria das categorias estendem-se por áreas como álgebra, teoria da recursividade, semântica formal, etc. 1268 | 1269 | Uma única operação exigida em uma categoria é a **composição**. Ouviremos falar muito disso ainda. 1270 | 1271 | - Uma classe de objetos `a`, `b`, `c`, ...; 1272 | - Para cada par de objetos a,b, uma classe de morfismos ou setas de a para b, denotados por `f:a -> b` (e neste caso se diz que a é o objeto origem e b é o objeto destino da seta); 1273 | - Para cada objeto a, um morfismo chamado identidade em a, `id_a:a -> a` que tem origem e destino em `a`; 1274 | - Uma operação de composição que associa a cada par de morfismos. 1275 | 1276 | ![imagem de uma função gigante de matemática apenas porque a zuera não tem limites](https://cldup.com/DgAjKvXx7W-1200x1200.png) 1277 | 1278 | #### Functor 1279 | 1280 | > A Functor is a function, given a value and a function, unwraps the values to get to its inner value(s), calls the given function with the inner value(s), wraps the returned values in a new structure, and returns the new structure. 1281 | 1282 | Vamos entender parte por parte: 1283 | 1284 | - *Functor* é uma função que irá receber um valor e uma função; 1285 | - Desencapsula[?] os valores para chegar a seu(s) valor(es) interno(s); 1286 | - Chama a função repassada com o(s) valor(es) interno(s); 1287 | - Encapsula os valores devolvidos em uma nova estrutura; 1288 | - e retorna a nova estrutura. 1289 | 1290 | ![meme realy?](https://cldup.com/ERM06kh3ki-2000x2000.jpeg) 1291 | 1292 | Sim eu sei que é basicamente a tradução do texto acima, bom então vamos ao que interessa, *códigoooooooooooo*: 1293 | 1294 | ```js 1295 | function plus1(value) { 1296 | return value + 1 1297 | }; 1298 | 1299 | function plus2(value) { 1300 | return value + 2 1301 | }; 1302 | ``` 1303 | 1304 | Criamos duas funções simples, `plus1` adiciona 1 ao `value` e `plus2` adiciona 2, agora vamos escrever uma função para que possamos usar qualquer uma dessas funções como e quando necessário: 1305 | 1306 | ```js 1307 | function F(value, fn) { 1308 | return fn(value) 1309 | }; 1310 | 1311 | F(1, plus1); // 2 1312 | ``` 1313 | 1314 | Essa função irá funcionar enquanto passarmos um inteiro, vamos tentar passar um *Array*: 1315 | 1316 | ```js 1317 | F([1, 2, 3], plus1); // '1,2,31' 1318 | ``` 1319 | 1320 | ![meme shit happens](https://cldup.com/g-9ZGuT22B-1200x1200.jpeg) 1321 | 1322 | E que bela **merda** aconteceu hein, passamos um *Array*, somamos com um inteiro e recebos uma *String*!!! 1323 | 1324 | ![meme pode isso Arnaldo?](https://cldup.com/5sahDi-dC0-1200x1200.jpeg) 1325 | 1326 | Nós queremos que F faça o trabalho "do jeito certo" e o "jeito certo" é manter a estrutura durante a operação. Mas o que significa **"manter a estrutura"**? 1327 | 1328 | Significa que **nossa função precisa "desencapsular"** o *Array* passado **e pegar seus elementos**. Depois precisa **chamar a função passada para cada elemento**. Então **encapsula os valores retornados em um novo *Array* e retorná-lo**. 1329 | 1330 | Isso não te lembra nenhuma funçãozinha não? 1331 | 1332 | ![meme pensando](https://cldup.com/bYyOR0OQpS-1200x1200.png) 1333 | 1334 | **SIM!** A função `map` é um *Functor*! 1335 | 1336 | ```js 1337 | [1, 2, 3].map(plus1); // [2, 3, 4] 1338 | ``` 1339 | 1340 | No caso do JasvaScript, `filter` é um *Functor* porque retorna um *Array*, entretando o `forEach` não é pois retorna `undefined`, ou seja, ele não mantém a estrutura. 1341 | 1342 | ```js 1343 | [1, 2, 3].map(plus1); 1344 | [2, 3, 4] 1345 | [1, 2, 3].filter(plus1) 1346 | [1, 2, 3] 1347 | [1, 2, 3].forEach(plus1); 1348 | undefined 1349 | ``` 1350 | 1351 | *Functors* são definidos como **"[homomorfismos](https://pt.wikipedia.org/wiki/Homomorfismo) entre categorias"**, vamos entender melhor esse significado: 1352 | 1353 | - Homo = mesmo, igual 1354 | - Morfismos = funções que mantém estrutura 1355 | - Categoria = tipo 1356 | 1357 | De acordo com a teoria, a função `F` é um *Functor* quando as duas funções comuns combináveis `f` e `g`, como no exemplo abaixo: 1358 | 1359 | ``` 1360 | F(x . y) = F(x) . F(y) 1361 | ``` 1362 | 1363 | Onde `.` indicam composição, ou seja, *Functors* precisam preservar a composição. 1364 | 1365 | Veremos mais sobre composição adiante. 1366 | 1367 | ##### Array Functor 1368 | 1369 | Como disse que o `map` é um *Functor* então vamos provar isso. 1370 | 1371 | ```js 1372 | function compose(f, g) { 1373 | return function(x) {return f(g(x))} 1374 | } 1375 | ``` 1376 | 1377 | Fazer composição de funções é criar uma chamada de um conjunto de funções, chamando a função seguinte, com resultados da função anterior. Note que a nossa função de composição acima funciona da direita para a esquerda. `g` é chamado pela primeira vez, em seguida, `f`. 1378 | 1379 | ```js 1380 | function plus2(value) { 1381 | console.log('plus2 value enter:', value); 1382 | return value + 2; 1383 | }; 1384 | 1385 | function plus1(value) { 1386 | console.log('plus1 value enter:', value); 1387 | return value + 1; 1388 | }; 1389 | 1390 | [1, 2, 3].map(compose(plus1, plus2)); // [ 4, 5, 6 ] 1391 | 1392 | plus2 value enter: 1 1393 | plus1 value enter: 3 1394 | plus2 value enter: 2 1395 | plus1 value enter: 4 1396 | plus2 value enter: 3 1397 | plus1 value enter: 5 1398 | [ 4, 5, 6 ] 1399 | 1400 | 1401 | ``` 1402 | 1403 | Percebeu o que aconteceu? Não? Vou separar melhor então: 1404 | 1405 | ``` 1406 | [ plus2 value enter: 1 (+ 2 = 3) & plus1 value enter: 3 (+ 1 = 4), // 4 1407 | plus2 value enter: 2 (+ 2 = 4) & plus1 value enter: 4 (+ 1 = 5), // 5 1408 | plus2 value enter: 3 (+ 2 = 5) & plus1 value enter: 5 (+ 1 = 6) // 6 1409 | ] 1410 | [ 4, 5, 6 ] 1411 | ``` 1412 | 1413 | Agora sim né? 1414 | 1415 | É o mesmo que compor usando 2 funções `map`: 1416 | 1417 | ```js 1418 | [1, 2, 3].map(plus2).map(plus1); // [ 4, 5, 6 ] 1419 | ``` 1420 | 1421 | ### Loops 1422 | 1423 | Antes de entrarmos nas propriedades funcionais propriamente ditas, vamos ver o porquê usar loops não é tão interessante quando possuímos o paradigma funcional em nossa linguagem. 1424 | 1425 | Vamos ver um clássico exemplo de um `for`: 1426 | 1427 | ```js 1428 | var animals = ["horse", "pig", "cow"]; 1429 | for(var i = 0, l = animals.length; i < l; i++) { 1430 | console.log("Animal: ", animals[i]); 1431 | }; 1432 | ``` 1433 | 1434 | Vamos refatorar usando um `while` reverso: 1435 | 1436 | ```js 1437 | var i = animals.length; 1438 | while(i--) { 1439 | console.log("Animal: ", animals[i]); 1440 | }; 1441 | ``` 1442 | 1443 | Agora vamos usar a forma funcional de loop: 1444 | 1445 | ```js 1446 | animals.forEach(function(animal) { 1447 | console.log("Animal: ", animal); 1448 | }) ; 1449 | ``` 1450 | 1451 | Perceba que agora em vez de iterarmos um número "fixo" incrementando ou decrementando um contador para que ao chegar no final ele saia do loop, mas isso pode esconder efeitos colaterais. 1452 | 1453 | Nesse último caso o programa está explicitamente iterando **em cima** do *Array* utilizado sem precisar gerenciar nenhum contador. E principalmente quando passamos variáveis como parametros. 1454 | 1455 | ```js 1456 | var Webschool = [ 1457 | {name: 'Suissa', course: 'JS Funcional', price: 0}, 1458 | {name: 'Suissa', course: 'Be MEAN', price: 600}, 1459 | {name: 'Caio Cutrim', course: 'Node.js', price: 0} 1460 | ]; 1461 | 1462 | function presentation(obj) { 1463 | return console.log( 'O professor ' + obj.name + ' dá o curso ' + obj.course + '.' ); 1464 | } 1465 | 1466 | function getPresentations(obj) { 1467 | for(var i = 0, l = obj.length; i < l; i++) { 1468 | presentation(obj[i]); 1469 | }; 1470 | }; 1471 | 1472 | getPresentations(Webschool); 1473 | // O professor Suissa dá o curso JS Funcional. 1474 | // O professor Suissa dá o curso Be MEAN. 1475 | // O professor Caio Cutrim dá o curso Node.js. 1476 | ``` 1477 | 1478 | Bom mas até ai sem problemas né? Porém e se agora eu queira adicionar um parametro novo nessa função `presentation` para que ela me mostre o valor do curso também, nesse caso passarei um `Boolean`. 1479 | 1480 | ```js 1481 | function presentation(obj, showPrice) { 1482 | var msg = 'O professor ' + obj.name + ' dá o curso ' + obj.course + '.'; 1483 | if(showPrice){ 1484 | msg = 'O professor ' + obj.name + ' dá o curso ' + obj.course + ' que custa R$' + obj.price + '.'; 1485 | } 1486 | return console.log(msg); 1487 | }; 1488 | 1489 | function getPresentations(obj, showPrice) { 1490 | for(var i = 0, l = obj.length; i < l; i++) { 1491 | presentation(obj[i], showPrice); 1492 | }; 1493 | }; 1494 | 1495 | getPresentations(Webschool, true); 1496 | ``` 1497 | 1498 | [ESCREVER EXEMPLO COM FOREACH] 1499 | 1500 | 1501 | [FALAR MAIS SOBRE OS EFEITOS COLATERAIS] 1502 | 1503 | 1504 | ### Recursion 1505 | 1506 | [Mostrar recursão em vez de loops] 1507 | 1508 | ###For/list comprehensions 1509 | 1510 | Material interessante em http://stackoverflow.com/questions/4964456/make-javascript-do-list-comprehension 1511 | 1512 | [Pegar ideias daqui] 1513 | ``` 1514 | 1515 | A list comprehension has a few parts to it. 1516 | 1517 | Selecting a set of something 1518 | From a set of Something 1519 | Filtered by Something 1520 | In JavaScript, as of ES5 (so I think that's supported in IE9+, Chrome and FF) you can use the map and filter functions on an array. 1521 | 1522 | You can do this with map and filter: 1523 | 1524 | var list = [1,2,3,4,5].filter(function(x){ return x < 4; }) 1525 | .map(function(x) { return 'foo ' + x; }); 1526 | 1527 | console.log(list); //["foo 1", "foo 2", "foo 3"] 1528 | That's about as good as it's going to get without setting up additional methods or using another framework. 1529 | 1530 | As for the specific question... 1531 | 1532 | With jQuery: 1533 | 1534 | $('input').map(function(i, x) { return x.name; }); 1535 | Without jQuery: 1536 | 1537 | var inputs = [].slice.call(document.getElementsByTagName('input'), 0), 1538 | names = inputs.map(function(x) { return x.name; }); 1539 | [].slice.call() is just to convert the NodeList to an Array. 1540 | ``` 1541 | 1542 | ### Immutability 1543 | 1544 | 1545 | 1546 | 1547 | ### Pure functions 1548 | 1549 | Funções puras são funções que se receberem um argumento retornarão o mesmo valor sem modificações, dado uma entrada ela sempre retornará o mesmo valor. Ela não pode modificar nenhum argumento passado nem gravar nenhum estado. 1550 | 1551 | Outra coisa muito boa de se trabalhar com *pure functions* é a transparência referecial. Um código será transparente referencialmente quando ele pode ser substituído pelo valor avaliado, sem alterar o comportamento do programa. 1552 | 1553 | Vamos ver um exemplo ([retirado daqui](https://github.com/Webschool-io/mostly-adequate-guide/blob/master/ch3.md)): 1554 | 1555 | ```js 1556 | var decrementHP = function(player) { 1557 | return player.set("hp", player.hp-1); 1558 | }; 1559 | 1560 | var isSameTeam = function(player1, player2) { 1561 | return player1.team === player2.team; 1562 | }; 1563 | 1564 | var punch = function(player, target) { 1565 | if(isSameTeam(player, target)) { 1566 | return target; 1567 | } else { 1568 | return decrementHP(target); 1569 | } 1570 | }; 1571 | 1572 | var jobe = Immutable.Map({name:"Jobe", hp:20, team: "red"}); 1573 | var michael = Immutable.Map({name:"Michael", hp:20, team: "green"}); 1574 | 1575 | punch(jobe, michael); 1576 | //=> Immutable.Map({name:"Michael", hp:19, team: "green"}) 1577 | ``` 1578 | 1579 | `decrementHP`, `isSameTeam` e `punch` são todos puros e, portanto, referencialmente transparente. Podemos usar uma técnica chamada de raciocínio equacional em que um substitutos "é igual para igual" raciocinar sobre o código. É um pouco como avaliar manualmente o código sem ter em conta as peculiaridades de avaliação programática. Usando transparência referencial, vamos jogar com este código um pouco. 1580 | 1581 | Primeiro vamos trocar a função isSameTeam: 1582 | 1583 | ```js 1584 | var punch = function(player, target) { 1585 | if(player.team === target.team) { 1586 | return target; 1587 | } else { 1588 | return decrementHP(target); 1589 | } 1590 | }; 1591 | ``` 1592 | 1593 | Desde os nossos dados sãp imutáveis, podemos simplesmente substituir as equipes com o seu valor real: 1594 | 1595 | ```js 1596 | var punch = function(player, target) { 1597 | if("red" === "green") { 1598 | return target; 1599 | } else { 1600 | return decrementHP(target); 1601 | } 1602 | }; 1603 | ``` 1604 | 1605 | Nós vemos que ela é falsa, neste caso, para que possamos remover o todo o código que não será executado: 1606 | 1607 | ```js 1608 | var punch = function(player, target) { 1609 | return decrementHP(target); 1610 | }; 1611 | ``` 1612 | 1613 | E se nós sequenciarmos `decrementHP`, vemos que, neste caso, torna-se uma chamada de `punch` para diminuir o `hp` por um. 1614 | 1615 | ```js 1616 | var punch = function(player, target) { 1617 | return target.set("hp", target.hp-1); 1618 | }; 1619 | ``` 1620 | 1621 | ### No side effects 1622 | 1623 | >A pure function is a function that, given the same input, will always return the same output and does not have any observable side effect. 1624 | 1625 | Vamos ilustrar essa afirmação utilizando como exemplo nossos conhecidos: `slice` e `splice`: 1626 | 1627 | ```js 1628 | var xs = [1,2,3,4,5]; 1629 | 1630 | // pure 1631 | xs.slice(0,3); 1632 | //=> [1,2,3] 1633 | 1634 | xs.slice(0,3); 1635 | //=> [1,2,3] 1636 | 1637 | xs.slice(0,3); 1638 | //=> [1,2,3] 1639 | 1640 | 1641 | // impure 1642 | xs.splice(0,3); 1643 | //=> [1,2,3] 1644 | 1645 | xs.splice(0,3); 1646 | //=> [4,5] 1647 | 1648 | xs.splice(0,3); 1649 | //=> [] 1650 | ``` 1651 | 1652 | Vamos ver outro exemplo: 1653 | 1654 | ```js 1655 | // impure 1656 | var minimum = 21; 1657 | 1658 | var checkAge = function(age) { 1659 | return age >= minimum; 1660 | }; 1661 | 1662 | // pure 1663 | var checkAge = function(age) { 1664 | var minimum = 21; 1665 | return age >= minimum; 1666 | }; 1667 | 1668 | ``` 1669 | 1670 | [EXPLICAR] 1671 | 1672 | ```js 1673 | var immutableState = Object.freeze({ 1674 | minimum: 21 1675 | }); 1676 | ``` 1677 | 1678 | [EXPLICAR] 1679 | 1680 | Mas o que é um efeito colateral (*side effect*)? Um pedaço de código segundo o qual uma variável é criada e está disponível ao longo de uma extensão quando ele não precisa de ser. Deixe-me mostrar-lhe alguns exemplos e como evitar esses efeitos colaterais indesejados: 1681 | 1682 | >Array.prototype.forEach() instead of for(var x = ...) 1683 | 1684 | Loop através de um *array* em JavaScript é tradicionalmente feito através de um *loop* `for`: 1685 | 1686 | ```js 1687 | var myArray = [1, 2, 3]; 1688 | 1689 | for(var x=0, length = myArray.length; x < length; x++) { 1690 | // ... 1691 | } 1692 | 1693 | // "x" and "length" são efeitos colaterais 1694 | ``` 1695 | 1696 | O efeito colateral deste padrão é, no mínimo, o índice incremental `x`, se não o `length`, elas estão disponíveis em todo o escopo. Métodos do *prototype* do *Array* como map, foreach, e outros nos evitam esses efeitos colaterais: 1697 | 1698 | ```js 1699 | [1, 2, 3].forEach(function(item, index, array) { 1700 | // No side effects! :) 1701 | }); 1702 | ``` 1703 | 1704 | [LER mais aqui http://davidwalsh.name/preventing-sideeffects-javascript] 1705 | 1706 | 1707 | >A *side effect* is a change of system state or *observable interaction* with the outside world that occurs during the calculation of a result. 1708 | 1709 | Efeitos colaterais podem incluir: 1710 | 1711 | + changing the file system 1712 | + inserting a record into a database 1713 | + making an http call 1714 | + mutations 1715 | + printing to the screen / logging 1716 | + obtaining user input 1717 | + querying the DOM 1718 | + accessing system state 1719 | 1720 | Efeitos colaterais desqualificam uma função de ser pura e isso faz sentido: funções puras, por definição, precisa sempre retornar a mesma saída dada a mesma entrada, o que não é posível garantir quando lidamos com *coisas* fora da nossa função. Vamos analisar porque devemos ter sempre a mesma saída com a mesma entrada, vamos ver um pouco de Matemática <3 básica. 1721 | 1722 | 1723 | ### Memoization 1724 | 1725 | ### Monoid 1726 | 1727 | ### Monads 1728 | 1729 | > "As monads vieram para resolver um problema que não tínhamos antes." - Douglas Crockford 1730 | 1731 | ![](https://cldup.com/S5tC18Ab_x-2000x2000.jpeg) 1732 | 1733 | Normalmente esse assunto *Monads* é tratado com certa "obscuridade" para quem não está familiarizado com Teoria das Categorias, mas bem na verdade ela só parece complexa na matemática. 1734 | 1735 | Na verdade *Monad* é um padrão de design usado para descrever computações como um série de passos. Elas são extensivamente usadas em linguagens de programação puramente funcional para gerenciar efeitos colaterais, mas também são usadas em linguagens multiparadigmas para controlar complexidade. 1736 | 1737 | *Monad* encapsulam tipos dando-les um comportamento adicional. Entenderemos isso melhor com código na sequência, porém antes precisamos conhecer quais são os componentes de uma *monad*: 1738 | 1739 | - unit: função que encpapsula um valor em um tipo aceitado pelas funções compostas 1740 | - bind: função que transforma qualquer função para que aceite o mesmo tipo que ela retorna, deixando-a pronta para composição 1741 | 1742 | > Nota: A função bind function não é a mesma que a função Function.prototype.bind Essa última é nativa do ES5 e é usada para criar uma série de funções ou funções parcialmente aplicadas com esse valor vinculado. 1743 | 1744 | #### Leis da *Monad* 1745 | 1746 | Uma *monad* deve obedecer as seguintes leis para ser válida: 1747 | 1748 | 1. bind(unit(x), f) === f(x) 1749 | 2. bind(monad, unit) === monad 1750 | 3. bind(bind(monad, f), g) === bind(monad, function(x) { return bind(f(x), g); }) 1751 | 1752 | Vamos começar com um [exemplo de *monad* do Douglas Crockford](https://gist.github.com/JackNova/4339141): 1753 | [COLOCAR LINK OFICIAL DO ARTIGO] 1754 | 1755 | ```js 1756 | function MONAD() { 1757 | return function unit(value) { 1758 | var monad = Object.create(null); 1759 | monad.bind = function (func) { 1760 | return func(value); 1761 | }; 1762 | return monad; 1763 | }; 1764 | } 1765 | ``` 1766 | 1767 | Vamos entender como esse código funciona. 1768 | 1769 | 1. a função `MONAD` retorna a função `unit` passando `value` como parâmetro 1770 | 2. cria uma `monad` que não herda nada 1771 | 3. adiciona o método `bind` na *monad* que recebe uma função `func` como parâmetro 1772 | 4. e retorna a chamada dessa função `func` 1773 | 5. passando `value` para ela que foi passada na função construtora `unit` 1774 | 6. retorna a *monad* 1775 | 1776 | A *monad* mais simples é a identidade, a qual não adiciona nenhuma informação ao valor, o valor é passado para a função `unit` que passará para as funções ligadas. 1777 | 1778 | ```js 1779 | var identity = MONAD(); 1780 | var monad = identity("JS FTW!"); 1781 | monad.bind(alert); 1782 | ``` 1783 | 1784 | [MOSTRAR AS LEIS DAS MONADS UAM A UMA] 1785 | 1786 | [MOSTRAR OUTROS EXEMPLOS DE MONADS] 1787 | 1788 | ### Pattern matching 1789 | 1790 | ### Tail call 1791 | 1792 | ## ES6 1793 | ### Array Comprehension 1794 | https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Array_comprehensions 1795 | http://ariya.ofilabs.com/2013/01/es6-and-array-comprehension.html 1796 | 1797 | 1798 | ## Alunos 1799 | 1800 | Quem utilizar esse material para estudo, peço que mande um pull request adicionado o seu nome na lista abaixo: 1801 | -------------------------------------------------------------------------------- /assets/images/array-filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/assets/images/array-filter.png -------------------------------------------------------------------------------- /assets/images/array-map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/assets/images/array-map.png -------------------------------------------------------------------------------- /assets/images/array-reduce.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/assets/images/array-reduce.png -------------------------------------------------------------------------------- /assets/images/map-filter-reduce-in-emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/assets/images/map-filter-reduce-in-emoji.png -------------------------------------------------------------------------------- /assets/images/map-reduce.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/assets/images/map-reduce.png -------------------------------------------------------------------------------- /assets/images/refatoracao01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/assets/images/refatoracao01.png -------------------------------------------------------------------------------- /assets/images/robo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/assets/images/robo.jpg -------------------------------------------------------------------------------- /assets/images/sanduba.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/assets/images/sanduba.png -------------------------------------------------------------------------------- /aulas/aula-04/exercicio-first-class-RESOLVIDO.js: -------------------------------------------------------------------------------- 1 | var numbers = [1,2,3,4,5,6,7,8,9,10]; 2 | var apenasPares = function(numero){ 3 | return !(numero % 2); 4 | } 5 | var resposta = numbers.filter(apenasPares) 6 | console.log('resposta: ', resposta); -------------------------------------------------------------------------------- /aulas/aula-04/exercicio-first-class-jfreitas.js: -------------------------------------------------------------------------------- 1 | var numbers = [1,2,3,4,5,6,7,8,9,10]; 2 | var apenasPares = function(numeros){ 3 | if (numeros % 2 == 0) return true 4 | } 5 | numbers.filter(apenasPares); -------------------------------------------------------------------------------- /aulas/aula-04/exercicio-high-order-01-resolvido.js: -------------------------------------------------------------------------------- 1 | /** 2 | Crie uma função que multiplique recebendo 3 parâmetros: 3 | 4 | function _multiplicar(op, valor, vezes){}; 5 | 6 | Onde `op` será a operação de `somar` do nosso exemplo passado. 7 | */ 8 | 9 | function somar(x, y) { 10 | return x + y; 11 | }; 12 | 13 | function _multiplicar(op, valor, vezes){ 14 | var sum = 0; 15 | for(var i=2; i <= vezes; i++){ 16 | sum += op(sum, valor); 17 | console.log('sum:', sum); 18 | } 19 | return sum; 20 | }; 21 | 22 | /** 23 | RESULTADO 24 | _multiplicar(somar, 2, 3) 25 | sum: 2 26 | sum: 6 27 | 6 28 | */ -------------------------------------------------------------------------------- /aulas/aula-04/exercicio-high-order-02-resolvido.js: -------------------------------------------------------------------------------- 1 | /** 2 | Depois de fazer isso para `multiplicar` faça também para `dividir`. 3 | Onde `op` será a operação de `somar` do nosso exemplo passado. 4 | */ 5 | 6 | function subtrair(x, y) { 7 | return x - y; 8 | }; 9 | 10 | 11 | function _dividir(op, valor, vezes){ 12 | var sum = valor; 13 | for(var i=1; i <= vezes; i++){ 14 | sum = op(sum, vezes); 15 | } 16 | return sum; 17 | }; 18 | 19 | /** 20 | RESULTADO 21 | _dividir(subtrair, 8, 2) 22 | 4 23 | */ 24 | -------------------------------------------------------------------------------- /aulas/currying/currying.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/aulas/currying/currying.js -------------------------------------------------------------------------------- /aulas/currying/operacoes-matematicas.js: -------------------------------------------------------------------------------- 1 | function somar(x, y) { 2 | return x + y; 3 | } 4 | 5 | function subtrair(x, y) { 6 | return x - y; 7 | } 8 | 9 | function multiplicar(x, y) { 10 | var total = 0; 11 | var contador = 0; 12 | while(x){ 13 | total = somar(total, y) 14 | x--; 15 | } 16 | return total; 17 | } 18 | 19 | function dividir(x, y) { 20 | var total = x; 21 | var contador = 1; 22 | if(x === y) { 23 | return 1; 24 | } 25 | while(total > 0){ 26 | console.log('total antes', total); 27 | total = subtrair(total, y); 28 | console.log('total depois', total); 29 | x--; 30 | } 31 | return total; 32 | } -------------------------------------------------------------------------------- /aulas/currying/sum.js: -------------------------------------------------------------------------------- 1 | function curry(fn) { 2 | var c = curry.bind(this, fn = fn.bind.apply(fn, [this].concat([].slice.call(arguments, 1)))); 3 | 4 | c.exec = fn; 5 | 6 | return c; 7 | } 8 | 9 | function sum() { 10 | return [].reduce.call(arguments, function (c, n) { 11 | return c + n; 12 | }); 13 | } 14 | 15 | console.log(curry(sum, 1, 2)(3)(4, 5)(6, 7, 8).exec()); 16 | -------------------------------------------------------------------------------- /aulas/lambda/exemplos-lamdas.md: -------------------------------------------------------------------------------- 1 | function(UNICORNIO) { 2 | return function(y) { 3 | return UNICORNIO; 4 | } 5 | } 6 | 7 | function(UNICORNIO) { 8 | return function(y) { 9 | return UNICORNIO * y; 10 | } 11 | } -------------------------------------------------------------------------------- /aulas/operacoes-matematicas/operacoes-matematicas.md: -------------------------------------------------------------------------------- 1 | # Matemática para programadores 2 | 3 | Nesse curso vamos aprender um pouco mais sobre os fundamentos da matemática, iniciaremos pelas suas duas operações básicas: 4 | 5 | - adição 6 | - subtração. 7 | 8 | 9 | E vou demonstrar como podemos compor essas duas para criarmos funções matemáticas mais complexas como: 10 | 11 | - mulplicação; 12 | - divisão; 13 | - exponenciação; 14 | - radiciação; 15 | - logarítmo; 16 | - etc. 17 | 18 | Além da base matemática para isso também iremos aprender e praticar muito a composição, que é uma caraterística clássica do paradigma funcional, onde compomos uma função mais complexa com outras menores. 19 | 20 | Vamos iniciar criando nossas duas primeiras funções básicas, que chamarei de *funções atômicas*, ok? 21 | 22 | ```js 23 | function somar(x, y) { 24 | return x + y; 25 | } 26 | 27 | function subtrair(x, y) { 28 | return x - y; 29 | } 30 | 31 | ``` 32 | 33 | Básico né? 34 | 35 | ```js 36 | > somar(333, 333); 37 | 666 38 | > subtrair(500, 80); 39 | 420 40 | ``` 41 | 42 | Beleza agora o próximo passo é criarmos as funções de: multiplicação e divisão. 43 | 44 | O algoritmo para solucionarmos esse problema é o seguinte: 45 | 46 | ```suissagol 47 | Recebo 2 parâmetros: 48 | 49 | - X 50 | - Y 51 | 52 | inicio o TOTAL da multiplicação com 0 53 | inicio o CONTADOR da multiplicação com 0 54 | 55 | enquanto X for maior que 0 faça 56 | TOTAL recebe o a soma de TOTAL e Y 57 | X recebe a subtração de X e 1 58 | 59 | retorna o TOTAL 60 | ``` 61 | 62 | Bom a variáel `total` será o retorno da nossa função onde receberá a adição de um número X, por Y vezes, por exemplo: `multiplicar(2,3) == 6`. Bom então vamos criar nossa função em JavaScript: 63 | 64 | ```js 65 | function multiplicar(x, y) { 66 | var total = 0; 67 | var contador = 0; 68 | while(x){ 69 | total = somar(total, y) 70 | x = subtrair(x,1); 71 | } 72 | return total; 73 | } 74 | ``` 75 | 76 | E para executarmos uma multiplicação precisamos fazer apenas o seguinte: 77 | 78 | ```js 79 | multiplicar(333,2) 80 | 666 81 | ``` 82 | 83 | Bem simples não? Agora vamos para a próxima função: divisão. 84 | 85 | Como a divisão é um pouco mais complexa vamos quebrar ela em dois problemas: 86 | 87 | - não existe divisão por 0; 88 | - um número dividido por ele mesmo é 1. 89 | 90 | O algoritmo para a solução desses problemas é o seguinte: 91 | 92 | ```suissagol 93 | Recebo 2 parâmetros: 94 | 95 | - X 96 | - Y 97 | 98 | inicio o TOTAL da multiplicação com X 99 | inicio o CONTADOR da multiplicação com 1 100 | 101 | se Y for diferente de 0 faça 102 | se X for igual a Y faça 103 | retorne 1; 104 | retorne falso; 105 | ``` 106 | 107 | Convertendo esse algoritmo para JavaScript temos: 108 | 109 | ```js 110 | function dividir(x, y) { 111 | var total = x; 112 | var contador = 1; 113 | 114 | if(y !== 0){ // não existe divisão por 0 115 | if(x === y) { // um número dividido por ele mesmo é 1 116 | return 1; 117 | } 118 | } 119 | return false; 120 | } 121 | ``` 122 | 123 | Agora testamos ela assim: 124 | 125 | ```js 126 | > dividir(10,10) 127 | 1 128 | > dividir(10,0) 129 | false 130 | ``` 131 | 132 | Bom e o que acontece se testarmos assim? 133 | 134 | ```js 135 | > dividir(10,2) 136 | false 137 | 138 | ``` 139 | 140 | Então precisamos resolver agora o caso geral e para resolvê-lo faremos o seguinte algoritmo: 141 | 142 | ```suissagol 143 | Recebo 2 parâmetros: 144 | 145 | - X 146 | - Y 147 | 148 | inicio o TOTAL da multiplicação com X 149 | inicio o CONTADOR da multiplicação com 1 150 | 151 | se Y for diferente de 0 faça 152 | se X for igual a Y faça 153 | retorne 1; 154 | retorne falso; 155 | ``` 156 | 157 | E finalizando ele em JavaScript fica dessa forma: 158 | 159 | ```js 160 | function dividir(x, y) { 161 | var total = x; 162 | var contador = 0; 163 | 164 | if(y !== 0){ // não existe divisão por 0 165 | if(x === y) { // um número dividido por ele mesmo é 1 166 | return 1; 167 | } 168 | while(total > 0) { 169 | total = total - y; 170 | contador = 1; 171 | } 172 | return contador; 173 | } 174 | return false; 175 | } 176 | ``` 177 | 178 | E agora rodamos ela: 179 | 180 | ```js 181 | > dividir(840,2) 182 | 420 183 | ``` 184 | 185 | ### Exercício 186 | 187 | **Refatore a divisão para utilizar as funções de somar subtrair, criadas anteriormente.** 188 | 189 | Agora após finalizarmos essas operações básicas podemos agora resolver funções mais complexas como a `porcentagem`. 190 | 191 | ## Porcentagem 192 | 193 | A porcentagem quer saber qual a parte de um total, por exemplo: 194 | 195 | > 1% 777 é 7 196 | > 5% de 100 é 5 197 | > 10% de 700 é 70 198 | > 50% de 840 é 420 199 | > 100% de 666 é 666 200 | 201 | Sabendo disso vamos criar o algoritmo para solucionar a porcentagem, vamos dividir ele em 2 problemas: 202 | 203 | - X % 0 = 0 204 | - X % X = 1 205 | 206 | ```suissagol 207 | Recebo 2 parâmetros: 208 | 209 | - X 210 | - Y 211 | 212 | inicio o TOTAL da multiplicação com X 213 | inicio o CONTADOR da multiplicação com 1 214 | 215 | se Y for diferente de 0 faça 216 | se X for igual a Y faça 217 | retorne 1; 218 | retorne falso; 219 | ``` 220 | 221 | ```js 222 | function porcentagem(x, y) { 223 | if(y === 0) { 224 | return 0; 225 | } 226 | if(x === x) { 227 | return 1; 228 | } 229 | return false; 230 | } 231 | ``` 232 | 233 | ```js 234 | > porcentagem(100,0) 235 | 0 236 | > porcentagem(100,100) 237 | 1 238 | ``` 239 | 240 | Beleza resolvemos esses dois casos, agora vamos resolver o caso geral: 241 | 242 | 243 | ### Algoritmo mental do Suissa 244 | 245 | Eu resolvo a porcentagem de uma forma um pouco diferente das ensinadas na escola, pois eu faço eu faço da seguinte forma: 246 | 247 | > Quanto é 16% de 18.731? 248 | 249 | O ensinado normalmente é algo assim: 250 | 251 | ```math 252 | 18600 * 0.14 = 2604 253 | ``` 254 | 255 | Porém ninguém, normal, faz isso de cabeça, eu faço assim: 256 | 257 | > Quando a porcentagem é menor que 10 eu divido por 1% e multiplico o valor pela poercentagem desejada. Por exemplo: 3% de 600 é 18. 258 | 259 | ```math 260 | 18600 * 0.01 = 186 261 | 186 * 3 = 558 // 3% 262 | ``` 263 | 264 | Agora quando a **porcentagem** é maior que 10% eu faço assim: 265 | 266 | > Quando a porcentagem é maior que 10 eu divido por 10% e multiplico o valor pela poercentagem desejada. Por exemplo: 22% de 18600 é 18. 267 | 268 | ```math 269 | 18600 * 0.20 = 3720 270 | 18600 * 0.01 = 186 271 | 186 * 2 =j 272 | 4092 273 | 18600 * 0.01 = 186 274 | 186 * 4 = 744 275 | 1860 + 744 = 2604 276 | ``` 277 | 278 | ## Exponenciação 279 | 280 | A **exponenciação** também conhecida por potência se dá por qual cálculo? 281 | 282 | > A base é multiplicada por ela mesmo pelo mesmo número da potência. 283 | 284 | Na **exponenciação** você tem: 285 | 286 | - uma `base`; 287 | - uma `potência`. 288 | 289 | Exemplo, na programação utilizamos o `^` para referenciar a potência: 290 | 291 | ```math 292 | 3 ˆ2 = 9// 3 elevado ao quadrado é igual a 9 293 | // 3 é a base 294 | // 2 é a potência 295 | 3(1) * 3(2) = 9 296 | ``` 297 | 298 | Então você pega a `base` e multiplica por ela mesmo o número de vezes da `potência`, como já sabemos como a multiplicação funciona vamos converter a exponenciação em multiplicação: 299 | 300 | ```math 301 | 3 ˆ4 = 81 302 | 3 * 3 * 3 * 3 = 81 303 | ``` 304 | 305 | Então podemos resolver esse cálculo com o seguinte algoritmo: 306 | 307 | ```suissagol 308 | Recebo 2 parâmetros: 309 | 310 | - X 311 | - Y 312 | 313 | inicio o TOTAL da multiplicação com X 314 | inicio o CONTADOR da multiplicação com 1 315 | 316 | se Y for diferente de 0 faça 317 | se X for igual a Y faça 318 | retorne 1; 319 | retorne falso; 320 | ``` 321 | 322 | Agora vamos transformar em JavaScript: 323 | 324 | ```js 325 | function exponenciar(x, y) { 326 | var total = x; 327 | var contador = x; 328 | while(contador){ 329 | total = multiplicar(total, x); 330 | contador = subtrair(contador,1); 331 | } 332 | return total; 333 | } 334 | ``` 335 | 336 | ## Radiciação 337 | 338 | Depois da **exponenciação** estudaremos a **radiciação** que é a famosa `RAIZ`, a raíz é o inverso da potência. Então podemos pensar assim, se: 339 | 340 | ```math 341 | 3 ˆ4 = 81 342 | 3 * 3 * 3 * 3 = 81 343 | ``` 344 | 345 | Então o inverso dessa potência é: 346 | 347 | ```math 348 | 81 raiz 3 = 4 349 | 81 / 3 = 27 350 | 27 / 3 = 9 351 | 9 / 3 = 3 352 | 3 / 3 = 1 353 | ``` 354 | 355 | Nesse caso dividimos o `81` por `3` até o resultado ser `1`, nesse caso dividimos o `81` que é a base por `3` que o ?????? até não podermos mais dividí-lo, nesse caso o resultado é 4 pois foi o número de passos necessários até reduzirmos o `81` para `1`. 356 | -------------------------------------------------------------------------------- /aulas/slides/JS Funcional FREE - slides - aula 1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/aulas/slides/JS Funcional FREE - slides - aula 1.pdf -------------------------------------------------------------------------------- /capa-email.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/capa-email.gif -------------------------------------------------------------------------------- /examples.js: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | Códigos de exemplo do mestre Halan Pinheiro https://github.com/halan 4 | https://gist.github.com/halan/c3c0ec1142b8d1bbf242939c238fbcab 5 | 6 | */ 7 | 8 | const reduce = (reducer, initial, [head, ...tail]) => 9 | head // condition to go or stop 10 | ? reduce(reducer, reducer(initial, head), tail) // recursion 11 | : initial // stop 12 | 13 | const map = (mapper, [head, ...tail]) => 14 | head // condition to go or stop 15 | ? [ mapper(head), ...map(mapper, tail) ] //recursion 16 | : [] // stop 17 | 18 | const filter = (predicate, [head, ...tail]) => 19 | head // condition to go or stop 20 | ? [ ...(predicate(head) ? [head] : []), ...filter(predicate, tail) ] // recursion 21 | : [] // stop 22 | 23 | const zip = ([head, ...tail], [head2, ...tail2]) => 24 | head // condition to go or stop 25 | ? [ [head, head2], ...zip(tail, tail2)] // recursion 26 | : [] // stop 27 | 28 | const find = (predicate, [head, ...tail]) => 29 | head // condition to go or stop 30 | ? predicate(head) ? head : find(predicate, tail) // recursion 31 | : undefined // stop 32 | 33 | const sort = ([head, ...tail]) => // quick sort (https://pt.wikipedia.org/wiki/Quicksort) 34 | head // condition to go or stop 35 | ? [...sort(filter(n => n <= head, tail)), head, ...sort(filter(n => n > head, tail))] // recursion (depends of filter) 36 | : [] // stop 37 | -------------------------------------------------------------------------------- /examples.md: -------------------------------------------------------------------------------- 1 | # Exemplos de funçoes conhecidas 2 | 3 | [Códigos de exemplo](https://gist.github.com/halan/c3c0ec1142b8d1bbf242939c238fbcab) do mestre [Halan Pinheiro](https://github.com/halan)! 4 | 5 | Como estou aprendendo bastante no grupo [Programacao Funcional Brasil](https://telegram.me/ProgramacaoFuncionalBrasil) com o [Halan Pinheiro](https://github.com/halan) então nada mais justo que eu ensine vocês também. 6 | 7 | ## Reduce 8 | 9 | ```js 10 | const reduce = (reducer, initial, [head, ...tail]) => 11 | head // condition to go or stop 12 | ? reduce(reducer, reducer(initial, head), tail) // recursion 13 | : initial // stop 14 | ``` 15 | 16 | ## Map 17 | 18 | ```js 19 | const map = (mapper, [head, ...tail]) => 20 | head // condition to go or stop 21 | ? [ mapper(head), ...map(mapper, tail) ] //recursion 22 | : [] // stop 23 | ``` 24 | 25 | ## Filter 26 | 27 | ```js 28 | const filter = (predicate, [head, ...tail]) => 29 | head // condition to go or stop 30 | ? [ ...(predicate(head) ? [head] : []), ...filter(predicate, tail) ] // recursion 31 | : [] // stop 32 | ``` 33 | 34 | ## Zip 35 | 36 | ```js 37 | const zip = ([head, ...tail], [head2, ...tail2]) => 38 | head // condition to go or stop 39 | ? [ [head, head2], ...zip(tail, tail2)] // recursion 40 | : [] // stop 41 | ``` 42 | 43 | ## Find 44 | 45 | ```js 46 | const find = (predicate, [head, ...tail]) => 47 | head // condition to go or stop 48 | ? predicate(head) ? head : find(predicate, tail) // recursion 49 | : undefined // stop 50 | ``` 51 | 52 | ## Sort 53 | 54 | ```js 55 | const sort = ([head, ...tail]) => // quick sort (https://pt.wikipedia.org/wiki/Quicksort) 56 | head // condition to go or stop 57 | ? [...sort(filter(n => n <= head, tail)), head, ...sort(filter(n => n > head, tail))] // recursion (depends of filter) 58 | : [] // stop 59 | ``` 60 | 61 | 62 | ## Distribuição Eletrônica 63 | 64 | Esses tempos atrás fiz [um scriptzinho](https://github.com/Webschool-io/Quimica-para-programadores/blob/master/distribui.js), antes de conhecer bem, para o fazer a [distribuição eletrônica dos elétrons]() e tinha percebido que ficou péssimo mas ainda não tinha a técnica suficiente em funcional para melhora-lo, olhem só: 65 | 66 | 67 | ```js 68 | 69 | let positions = { 70 | linha: 0, 71 | coluna: 0 72 | } 73 | module.exports = (distribuicao) => { 74 | 75 | const pulaLinha = (linha, coluna) => [linha+1, coluna] 76 | const pulaLinhaIniciaColuna = (linha, coluna) => [linha+1, 0] 77 | const pulaLinhaColuna = (linha, coluna) => [linha+1, coluna+1] 78 | const pulaLinhaDiminuiColuna = (linha, coluna) => [linha+1, coluna-1] 79 | const voltaLinhaContinuaColuna = (linha, coluna) => [linha-1, coluna] 80 | const voltaLinhaVoltaColuna = (linha, coluna) => [linha-1, coluna-1] 81 | const volta2LinhasUltimaColuna = (linha, coluna) => [linha-2, coluna] 82 | const continuaLinhaAndaColuna = (linha, coluna) => [linha, coluna+1] 83 | const isFinal = (linha, coluna) => { 84 | const ultimaLinha = (linha === distribuicao.length-1) 85 | const ultimaColuna = (coluna === distribuicao[linha].length-1) 86 | return (ultimaLinha && ultimaColuna) 87 | } 88 | 89 | const getNextElement = (parOrdenado) => { 90 | let linha = parOrdenado[0] 91 | let coluna = parOrdenado[1] 92 | 93 | if(!Array.isArray(parOrdenado) || isFinal(linha, coluna)) return false 94 | if(!linha) { // === 0 95 | if(!coluna) return pulaLinha(linha, coluna) // === 0 96 | else return pulaLinhaColuna(linha, coluna) 97 | } 98 | if(linha === 1 || linha === 2) { 99 | if(!coluna) return continuaLinhaAndaColuna(linha, coluna) 100 | if(coluna === 1 ) return pulaLinhaIniciaColuna(linha, coluna) 101 | // Se coluna é a ultima posição da linha 102 | let ultima = distribuicao[linha].length-1 103 | if(coluna === ultima) { 104 | return pulaLinhaDiminuiColuna(positions.linha, positions.coluna) 105 | } 106 | } 107 | if(linha > 2 && linha < 5) { 108 | // Pego a última posição da linha anterior 109 | let tam = distribuicao[linha-1].length-1 110 | if(!coluna){ 111 | if(tam < 3) { 112 | positions.linha = linha-1 113 | positions.coluna = tam 114 | return voltaLinhaContinuaColuna(linha, tam) 115 | } 116 | else { 117 | positions.linha = linha-1 118 | positions.coluna = tam-1 119 | return voltaLinhaContinuaColuna(linha-1, tam-1) 120 | } 121 | } 122 | else return pulaLinhaDiminuiColuna(linha, coluna) 123 | } 124 | if(linha >= 5){ 125 | let ultimaPosicao = distribuicao[linha-2].length-1 126 | if(!coluna && ultimaPosicao > 2) return volta2LinhasUltimaColuna(linha, ultimaPosicao) 127 | if(coluna) return pulaLinhaDiminuiColuna(linha, coluna) 128 | } 129 | } 130 | 131 | const start = [0,0] 132 | console.log(distribuicao[start[0]][start[1]]) 133 | let next = getNextElement(start) 134 | if(next) console.log(distribuicao[next[0]][next[1]]) 135 | while(next) { 136 | next = getNextElement(next) 137 | if(next) console.log(distribuicao[next[0]][next[1]]) 138 | } 139 | } 140 | ``` 141 | 142 | **Claro que dá para perceber que rola melhorar e muito isso e a primeira dica que ele me deu é que poderia limpar alguns `if`s usando um `switch`. 143 | 144 | [Primeira refatoração dele](https://gist.github.com/halan/dd2091d5e3723c1ba68ce886ecca8eed) ficou assim: 145 | 146 | 147 | ```js 148 | module.exports = (distribuicao) => { 149 | 150 | const pulaLinha = (linha, coluna) => [linha+1, coluna] 151 | 152 | const pulaLinhaIniciaColuna = (linha, coluna) => [linha+1, 0] 153 | 154 | const pulaLinhaColuna = (linha, coluna) => [linha+1, coluna+1] 155 | 156 | const pulaLinhaDiminuiColuna = (linha, coluna) => [linha+1, coluna-1] 157 | 158 | const voltaLinhaContinuaColuna = (linha, coluna) => [linha-1, coluna] 159 | 160 | const voltaLinhaVoltaColuna = (linha, coluna) => [linha-1, coluna-1] 161 | 162 | const volta2LinhasUltimaColuna = (linha, coluna) => [linha-2, coluna] 163 | 164 | const continuaLinhaAndaColuna = (linha, coluna) => [linha, coluna+1] 165 | 166 | 167 | // Aqui no meio são funções puras, podem ser refatoradas com composição 168 | 169 | // const isFinal = (linha, coluna) => { 170 | // const ultimaLinha = (linha === distribuicao.length-1) 171 | // const ultimaColuna = (coluna === distribuicao[linha].length-1) 172 | // return (ultimaLinha && ultimaColuna) 173 | // } 174 | 175 | const atualizaPosition = (linha, tam) => 176 | ({linha: linha-1, coluna: tam < 3 ? tam : tam-1}) 177 | 178 | const quandoEntreDuaseCincoLinhas = (linha, coluna, position) => ( 179 | coluna ? 180 | [pulaLinhaDiminuiColuna(linha, coluna), position] : 181 | quandoEntreDuaseCincoLinhasComColuna(linha, distribuicao[linha-1].length-1) 182 | ) 183 | 184 | const quandoEntreDuaseCincoLinhasComColuna = (linha, tam) => ( 185 | tam < 3 ? // esse condicional pode ir pra dentro de uma função pra chamar o voltaLinhaContinuaColuna 186 | [voltaLinhaContinuaColuna(linha, tam), atualizaPosition(linha, tam)] : 187 | [voltaLinhaContinuaColuna(linha-1, tam-1), atualizaPosition(linha, tam)] 188 | ) 189 | 190 | const quandoMaiorQueCinco = (linha, coluna) => ( 191 | coluna ? 192 | pulaLinhaDiminuiColuna(linha, coluna) : 193 | volta2LinhasUltimaColuna(linha, distribuicao[linha-2].length-1) 194 | ) 195 | 196 | const pulaLinhaOuColuna = (linha, coluna) => 197 | !coluna ? pulaLinha(linha, coluna) : pulaLinhaColuna(linha, coluna) 198 | 199 | const pulaLinhaVaiParaColuna = (linha, tam) => 200 | voltaLinhaContinuaColuna(linha-1, tam) 201 | 202 | 203 | const quandoUmaOuDuasLinhas = (linha, coluna, positions) => { 204 | switch(coluna) { 205 | case 0: 206 | return continuaLinhaAndaColuna(linha, coluna) 207 | case 1: 208 | return pulaLinhaIniciaColuna(linha, coluna) 209 | default: 210 | //if(coluna === distribuicao[linha].length-1) { // Ou coloca um else nesse cara, ou não precisa dele. 211 | return pulaLinhaDiminuiColuna(positions.linha, positions.coluna) 212 | //} 213 | } 214 | } 215 | 216 | //----------------------------------------------- 217 | // 218 | 219 | const getNextElement = ([[linha, coluna], positions]) => { 220 | switch(true) { 221 | //case isFinal(linha, coluna): // em tese podemos apagar essa condição, o default do switch vai dar conta 222 | // return [false, positions] 223 | 224 | case !linha: 225 | return [pulaLinhaOuColuna(linha, coluna), positions] 226 | 227 | case linha === 1 || linha === 2: 228 | return [quandoUmaOuDuasLinhas(linha, coluna, positions), positions] 229 | 230 | case linha > 2 && linha < 5: 231 | return quandoEntreDuaseCincoLinhas(linha, coluna, positions) // <-- essa é a única que atualiza positions! 232 | 233 | case linha >= 5: 234 | return [quandoMaiorQueCinco(linha, coluna), positions] 235 | 236 | default: 237 | return [false, positions] 238 | } 239 | } 240 | 241 | const flatten = arr => 242 | arr.reduce( (result, subarr) => 243 | [...result, ...subarr], []) 244 | 245 | const print = arr => arr.forEach( v => console.log(v) ) 246 | 247 | const getValue = ([x, y]) => distribuicao[x][y] 248 | 249 | 250 | const start = [0,0] 251 | const posicaoInicial = { linha: 0, coluna: 0 } 252 | const initialState = { result: [], params: [start, posicaoInicial] } 253 | 254 | 255 | const step = ({ 256 | result, 257 | params: [next, position] 258 | })=> ({ // dá pra fazer melhor, mandando o segundo parâmetro e utilizando ele dentro do cálculo, imagino que isso pode simplificar algumas funções 259 | result: [...result, getValue(next)], 260 | params: getNextElement([next, position]) 261 | }) 262 | 263 | const solved = flatten(distribuicao) 264 | .reduce(step, initialState).result 265 | 266 | print(solved) 267 | } 268 | ``` 269 | 270 | [Solução final dele](https://github.com/halan/atomic-dist/blob/master/distribution.js): 271 | 272 | ```js 273 | const step = (x, y) => (!y) 274 | ? [ Math.ceil((x+1) / 2), Math.floor((x+1) / 2) ] 275 | : [ x+1, Math.max(y-1, 0) ] 276 | 277 | const mount = ([head, ...tail], last) => equalCoord(head, last) 278 | ? [ head, ...tail ] 279 | : [ ...mount([step(...head)], last), head, ...tail ] 280 | 281 | const equalCoord = ([x, y], [x2, y2]) => x === x2 && y === y2 282 | 283 | const initCoord = [0, 0] 284 | 285 | const getCoord = (arr, [x, y]) => arr[x][y] 286 | 287 | const getLastCoord = (input) => [input.length-1, input.slice(-1)[0].length-1] 288 | 289 | const orderedCoords = (input) => mount([initCoord], getLastCoord(input)).reverse() 290 | 291 | const coordsToValues = (coords, input) => coords.map(getCoord.bind(null, input)) 292 | 293 | const distribution = (input) => coordsToValues(orderedCoords(input), input) 294 | 295 | module.exports = distribution 296 | 297 | 298 | const order = arr => ( 299 | Array.apply(null, Array(count(arr))) 300 | .reduce( step, { source: arr, result: []}) 301 | .result 302 | ) 303 | 304 | module.exports = order 305 | ``` 306 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Exemplos de funções conhecidas e como cria-las 2 | 3 | 4 | ```js 5 | [🍞, 🍗].reduce((sanduba, ingrediente) => 🍔) 6 | .concat( 7 | [🍏, 🍎, 🍐, 🍊, 🍋, 🍌] 8 | .filter((fruta) => fruta === 🍎 || fruta === 🍋) 9 | .reduce((suco, fruta) => 🍹)) 10 | ``` 11 | 12 | Os [códigos de exemplo funcionais](https://gist.github.com/halan/c3c0ec1142b8d1bbf242939c238fbcab) são do mestre [Halan Pinheiro](https://github.com/halan)! Eu apenas irei mostrar como criar as funções de forma imperativa e depois analisando os códigos do Halan mostrarei como refatorarmos para chegarmos à esse resultado maravilhosamente **funcional**. 13 | 14 | Como estou aprendendo bastante no grupo [Programacao Funcional Brasil](https://telegram.me/ProgramacaoFuncionalBrasil) com o [Halan Pinheiro](https://github.com/halan) então nada mais justo que eu ensine vocês também. 15 | 16 | Antes de entrarmos nas funções podemos fazer uma analogia simples do `map` & `reduce` com a fabricação de sanduíches, veja a imagem abaixo: 17 | 18 | ![](https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/master/assets/images/map-reduce.png) 19 | 20 | Caso queira outro exemplo mais lúdico e com o `filter` também aí vai: 21 | 22 | 23 | ![map filter reduce in emoji](https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/master/assets/images/map-filter-reduce-in-emoji.png) 24 | 25 | 26 | ## [Map](https://github.com/Webschool-io/workshop-js-funcional-free/blob/master/examples/map.md) 27 | 28 | **Explicação completa [AQUI](https://github.com/Webschool-io/workshop-js-funcional-free/blob/master/examples/map.md)!** 29 | 30 | ```js 31 | const map = (mapper, [head, ...tail]) => 32 | head // condition to go or stop 33 | ? [ mapper(head), ...map(mapper, tail) ] //recursion 34 | : [] // stop 35 | ``` 36 | 37 | 38 | 39 | ![](https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/master/assets/images/array-map.png) 40 | 41 | 42 | ## [Filter](https://github.com/Webschool-io/workshop-js-funcional-free/blob/master/examples/filter.md) 43 | 44 | 45 | **Explicação completa [AQUI](https://github.com/Webschool-io/workshop-js-funcional-free/blob/master/examples/filter.md)!** 46 | 47 | 48 | ```js 49 | const filter = (predicate, [head, ...tail]) => 50 | head // condition to go or stop 51 | ? [ ...(predicate(head) ? [head] : []), ...filter(predicate, tail) ] // recursion 52 | : [] // stop 53 | ``` 54 | 55 | 56 | ![](https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/master/assets/images/array-filter.png) 57 | 58 | 59 | ## [Reduce](https://github.com/Webschool-io/workshop-js-funcional-free/blob/master/examples/reduce.md) 60 | 61 | **Explicação completa [AQUI](https://github.com/Webschool-io/workshop-js-funcional-free/blob/master/examples/reduce.md)!** 62 | 63 | 64 | ```js 65 | const reduce = (reducer, initial, [head, ...tail]) => 66 | head // condition to go or stop 67 | ? reduce(reducer, reducer(initial, head), tail) // recursion 68 | : initial // stop 69 | ``` 70 | 71 | ![](https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/master/assets/images/array-reduce.png) 72 | 73 | 74 | 75 | ## Zip 76 | 77 | ```js 78 | const zip = ([head, ...tail], [head2, ...tail2]) => 79 | head // condition to go or stop 80 | ? [ [head, head2], ...zip(tail, tail2)] // recursion 81 | : [] // stop 82 | ``` 83 | 84 | ## Find 85 | 86 | ```js 87 | const find = (predicate, [head, ...tail]) => 88 | head // condition to go or stop 89 | ? predicate(head) ? head : find(predicate, tail) // recursion 90 | : undefined // stop 91 | ``` 92 | 93 | ## Sort 94 | 95 | ```js 96 | const sort = ([head, ...tail]) => // quick sort (https://pt.wikipedia.org/wiki/Quicksort) 97 | head // condition to go or stop 98 | ? [...sort(filter(n => n <= head, tail)), head, ...sort(filter(n => n > head, tail))] // recursion (depends of filter) 99 | : [] // stop 100 | ``` 101 | 102 | 103 | ## Distribuição Eletrônica 104 | 105 | Esses tempos atrás fiz [um scriptzinho](https://github.com/Webschool-io/Quimica-para-programadores/blob/master/distribui.js), antes de conhecer bem, para o fazer a [distribuição eletrônica dos elétrons]() e tinha percebido que ficou péssimo mas ainda não tinha a técnica suficiente em funcional para melhora-lo, olhem só: 106 | 107 | 108 | ```js 109 | 110 | let positions = { 111 | linha: 0, 112 | coluna: 0 113 | } 114 | module.exports = (distribuicao) => { 115 | 116 | const pulaLinha = (linha, coluna) => [linha+1, coluna] 117 | const pulaLinhaIniciaColuna = (linha, coluna) => [linha+1, 0] 118 | const pulaLinhaColuna = (linha, coluna) => [linha+1, coluna+1] 119 | const pulaLinhaDiminuiColuna = (linha, coluna) => [linha+1, coluna-1] 120 | const voltaLinhaContinuaColuna = (linha, coluna) => [linha-1, coluna] 121 | const voltaLinhaVoltaColuna = (linha, coluna) => [linha-1, coluna-1] 122 | const volta2LinhasUltimaColuna = (linha, coluna) => [linha-2, coluna] 123 | const continuaLinhaAndaColuna = (linha, coluna) => [linha, coluna+1] 124 | const isFinal = (linha, coluna) => { 125 | const ultimaLinha = (linha === distribuicao.length-1) 126 | const ultimaColuna = (coluna === distribuicao[linha].length-1) 127 | return (ultimaLinha && ultimaColuna) 128 | } 129 | 130 | const getNextElement = (parOrdenado) => { 131 | let linha = parOrdenado[0] 132 | let coluna = parOrdenado[1] 133 | 134 | if(!Array.isArray(parOrdenado) || isFinal(linha, coluna)) return false 135 | if(!linha) { // === 0 136 | if(!coluna) return pulaLinha(linha, coluna) // === 0 137 | else return pulaLinhaColuna(linha, coluna) 138 | } 139 | if(linha === 1 || linha === 2) { 140 | if(!coluna) return continuaLinhaAndaColuna(linha, coluna) 141 | if(coluna === 1 ) return pulaLinhaIniciaColuna(linha, coluna) 142 | // Se coluna é a ultima posição da linha 143 | let ultima = distribuicao[linha].length-1 144 | if(coluna === ultima) { 145 | return pulaLinhaDiminuiColuna(positions.linha, positions.coluna) 146 | } 147 | } 148 | if(linha > 2 && linha < 5) { 149 | // Pego a última posição da linha anterior 150 | let tam = distribuicao[linha-1].length-1 151 | if(!coluna){ 152 | if(tam < 3) { 153 | positions.linha = linha-1 154 | positions.coluna = tam 155 | return voltaLinhaContinuaColuna(linha, tam) 156 | } 157 | else { 158 | positions.linha = linha-1 159 | positions.coluna = tam-1 160 | return voltaLinhaContinuaColuna(linha-1, tam-1) 161 | } 162 | } 163 | else return pulaLinhaDiminuiColuna(linha, coluna) 164 | } 165 | if(linha >= 5){ 166 | let ultimaPosicao = distribuicao[linha-2].length-1 167 | if(!coluna && ultimaPosicao > 2) return volta2LinhasUltimaColuna(linha, ultimaPosicao) 168 | if(coluna) return pulaLinhaDiminuiColuna(linha, coluna) 169 | } 170 | } 171 | 172 | const start = [0,0] 173 | console.log(distribuicao[start[0]][start[1]]) 174 | let next = getNextElement(start) 175 | if(next) console.log(distribuicao[next[0]][next[1]]) 176 | while(next) { 177 | next = getNextElement(next) 178 | if(next) console.log(distribuicao[next[0]][next[1]]) 179 | } 180 | } 181 | ``` 182 | 183 | **Claro que dá para perceber que rola melhorar e muito isso e a primeira dica que ele me deu é que poderia limpar alguns `if`s usando um `switch`. 184 | 185 | 186 | ### Distribuição Eletrônica - Primeira refatorada 187 | 188 | 189 | [Primeira refatoração dele](https://gist.github.com/halan/dd2091d5e3723c1ba68ce886ecca8eed) ficou assim: 190 | 191 | 192 | ```js 193 | module.exports = (distribuicao) => { 194 | 195 | const pulaLinha = (linha, coluna) => [linha+1, coluna] 196 | 197 | const pulaLinhaIniciaColuna = (linha, coluna) => [linha+1, 0] 198 | 199 | const pulaLinhaColuna = (linha, coluna) => [linha+1, coluna+1] 200 | 201 | const pulaLinhaDiminuiColuna = (linha, coluna) => [linha+1, coluna-1] 202 | 203 | const voltaLinhaContinuaColuna = (linha, coluna) => [linha-1, coluna] 204 | 205 | const voltaLinhaVoltaColuna = (linha, coluna) => [linha-1, coluna-1] 206 | 207 | const volta2LinhasUltimaColuna = (linha, coluna) => [linha-2, coluna] 208 | 209 | const continuaLinhaAndaColuna = (linha, coluna) => [linha, coluna+1] 210 | 211 | 212 | // Aqui no meio são funções puras, podem ser refatoradas com composição 213 | 214 | // const isFinal = (linha, coluna) => { 215 | // const ultimaLinha = (linha === distribuicao.length-1) 216 | // const ultimaColuna = (coluna === distribuicao[linha].length-1) 217 | // return (ultimaLinha && ultimaColuna) 218 | // } 219 | 220 | const atualizaPosition = (linha, tam) => 221 | ({linha: linha-1, coluna: tam < 3 ? tam : tam-1}) 222 | 223 | const quandoEntreDuaseCincoLinhas = (linha, coluna, position) => ( 224 | coluna ? 225 | [pulaLinhaDiminuiColuna(linha, coluna), position] : 226 | quandoEntreDuaseCincoLinhasComColuna(linha, distribuicao[linha-1].length-1) 227 | ) 228 | 229 | const quandoEntreDuaseCincoLinhasComColuna = (linha, tam) => ( 230 | tam < 3 ? // esse condicional pode ir pra dentro de uma função pra chamar o voltaLinhaContinuaColuna 231 | [voltaLinhaContinuaColuna(linha, tam), atualizaPosition(linha, tam)] : 232 | [voltaLinhaContinuaColuna(linha-1, tam-1), atualizaPosition(linha, tam)] 233 | ) 234 | 235 | const quandoMaiorQueCinco = (linha, coluna) => ( 236 | coluna ? 237 | pulaLinhaDiminuiColuna(linha, coluna) : 238 | volta2LinhasUltimaColuna(linha, distribuicao[linha-2].length-1) 239 | ) 240 | 241 | const pulaLinhaOuColuna = (linha, coluna) => 242 | !coluna ? pulaLinha(linha, coluna) : pulaLinhaColuna(linha, coluna) 243 | 244 | const pulaLinhaVaiParaColuna = (linha, tam) => 245 | voltaLinhaContinuaColuna(linha-1, tam) 246 | 247 | 248 | const quandoUmaOuDuasLinhas = (linha, coluna, positions) => { 249 | switch(coluna) { 250 | case 0: 251 | return continuaLinhaAndaColuna(linha, coluna) 252 | case 1: 253 | return pulaLinhaIniciaColuna(linha, coluna) 254 | default: 255 | //if(coluna === distribuicao[linha].length-1) { // Ou coloca um else nesse cara, ou não precisa dele. 256 | return pulaLinhaDiminuiColuna(positions.linha, positions.coluna) 257 | //} 258 | } 259 | } 260 | 261 | //----------------------------------------------- 262 | // 263 | 264 | const getNextElement = ([[linha, coluna], positions]) => { 265 | switch(true) { 266 | //case isFinal(linha, coluna): // em tese podemos apagar essa condição, o default do switch vai dar conta 267 | // return [false, positions] 268 | 269 | case !linha: 270 | return [pulaLinhaOuColuna(linha, coluna), positions] 271 | 272 | case linha === 1 || linha === 2: 273 | return [quandoUmaOuDuasLinhas(linha, coluna, positions), positions] 274 | 275 | case linha > 2 && linha < 5: 276 | return quandoEntreDuaseCincoLinhas(linha, coluna, positions) // <-- essa é a única que atualiza positions! 277 | 278 | case linha >= 5: 279 | return [quandoMaiorQueCinco(linha, coluna), positions] 280 | 281 | default: 282 | return [false, positions] 283 | } 284 | } 285 | 286 | const flatten = arr => 287 | arr.reduce( (result, subarr) => 288 | [...result, ...subarr], []) 289 | 290 | const print = arr => arr.forEach( v => console.log(v) ) 291 | 292 | const getValue = ([x, y]) => distribuicao[x][y] 293 | 294 | 295 | const start = [0,0] 296 | const posicaoInicial = { linha: 0, coluna: 0 } 297 | const initialState = { result: [], params: [start, posicaoInicial] } 298 | 299 | 300 | const step = ({ 301 | result, 302 | params: [next, position] 303 | })=> ({ // dá pra fazer melhor, mandando o segundo parâmetro e utilizando ele dentro do cálculo, imagino que isso pode simplificar algumas funções 304 | result: [...result, getValue(next)], 305 | params: getNextElement([next, position]) 306 | }) 307 | 308 | const solved = flatten(distribuicao) 309 | .reduce(step, initialState).result 310 | 311 | print(solved) 312 | } 313 | ``` 314 | 315 | O tal do `switch` ficou aqui: 316 | 317 | ```js 318 | 319 | const getNextElement = ([[linha, coluna], positions]) => { 320 | switch(true) { 321 | //case isFinal(linha, coluna): // em tese podemos apagar essa condição, o default do switch vai dar conta 322 | // return [false, positions] 323 | 324 | case !linha: 325 | return [pulaLinhaOuColuna(linha, coluna), positions] 326 | 327 | case linha === 1 || linha === 2: 328 | return [quandoUmaOuDuasLinhas(linha, coluna, positions), positions] 329 | 330 | case linha > 2 && linha < 5: 331 | return quandoEntreDuaseCincoLinhas(linha, coluna, positions) // <-- essa é a única que atualiza positions! 332 | 333 | case linha >= 5: 334 | return [quandoMaiorQueCinco(linha, coluna), positions] 335 | 336 | default: 337 | return [false, positions] 338 | } 339 | } 340 | ``` 341 | 342 | **Veja que louco esse `switch(true)`, que o caso eu nunca tinha usado assim, deixando a escolha de qual função executar nos `case`s**: 343 | 344 | ```js 345 | 346 | case !linha: 347 | return [pulaLinhaOuColuna(linha, coluna), positions] 348 | 349 | case linha === 1 || linha === 2: 350 | return [quandoUmaOuDuasLinhas(linha, coluna, positions), positions] 351 | 352 | case linha > 2 && linha < 5: 353 | return quandoEntreDuaseCincoLinhas(linha, coluna, positions) // <-- essa é a única que atualiza positions! 354 | 355 | case linha >= 5: 356 | return [quandoMaiorQueCinco(linha, coluna), positions] 357 | 358 | default: 359 | return [false, positions] 360 | 361 | ``` 362 | 363 | 364 | ### Distribuição Eletrônica - Solução Final 365 | 366 | Porém depois dessa refatoração ele apelou fortemente para técnicas utilizando `head` e `tail` que são largamente usadas na Programação Funcional e as quais eu ainda não conheço bem, por isso usarei muito os códigos dele para estudar e depois ensinar vocês. 367 | 368 | [Solução final dele](https://github.com/halan/atomic-dist/blob/master/distribution.js): 369 | 370 | ```js 371 | const step = (x, y) => (!y) 372 | ? [ Math.ceil((x+1) / 2), Math.floor((x+1) / 2) ] 373 | : [ x+1, Math.max(y-1, 0) ] 374 | 375 | const mount = ([head, ...tail], last) => equalCoord(head, last) 376 | ? [ head, ...tail ] 377 | : [ ...mount([step(...head)], last), head, ...tail ] 378 | 379 | const equalCoord = ([x, y], [x2, y2]) => x === x2 && y === y2 380 | 381 | const initCoord = [0, 0] 382 | 383 | const getCoord = (arr, [x, y]) => arr[x][y] 384 | 385 | const getLastCoord = (input) => [input.length-1, input.slice(-1)[0].length-1] 386 | 387 | const orderedCoords = (input) => mount([initCoord], getLastCoord(input)).reverse() 388 | 389 | const coordsToValues = (coords, input) => coords.map(getCoord.bind(null, input)) 390 | 391 | const distribution = (input) => coordsToValues(orderedCoords(input), input) 392 | 393 | module.exports = distribution 394 | 395 | 396 | const order = arr => ( 397 | Array.apply(null, Array(count(arr))) 398 | .reduce( step, { source: arr, result: []}) 399 | .result 400 | ) 401 | 402 | module.exports = order 403 | ``` 404 | -------------------------------------------------------------------------------- /examples/actions/filter.funcional.js: -------------------------------------------------------------------------------- 1 | const filter = (predicate, [head, ...tail]) => 2 | head // condition to go or stop 3 | ? [ ...(predicate(head) ? [head] : []), ...filter(predicate, tail) ] // recursion 4 | : [] // stop 5 | 6 | module.exports = filter -------------------------------------------------------------------------------- /examples/actions/filter.js: -------------------------------------------------------------------------------- 1 | const isArrayLike = (value) => !!(value != null 2 | && value != undefined 3 | && value.length 4 | && Array.isArray(value)) 5 | 6 | const filter = (values, fn) => { 7 | 8 | if (!isArrayLike(values)) throw new TypeError('Não é Array') 9 | 10 | let arr = [] 11 | 12 | for (let i=0; i (head) 2 | ? [ ...(fn(head) ? [head] : []), 3 | ...filter(tail, fn)] 4 | : [] 5 | 6 | module.exports = filter -------------------------------------------------------------------------------- /examples/actions/map.funcional.js: -------------------------------------------------------------------------------- 1 | const map = (mapper, [head, ...tail]) => 2 | head // condition to go or stop 3 | ? [ mapper(head), ...map(mapper, tail) ] //recursion 4 | : [] // stop 5 | 6 | module.exports = map -------------------------------------------------------------------------------- /examples/actions/map.js: -------------------------------------------------------------------------------- 1 | const isArrayLike = (value) => !!(value != null 2 | && value != undefined 3 | && value.length 4 | && Array.isArray(value)) 5 | 6 | const map = (values, fn) => { 7 | 8 | if (!isArrayLike(values)) throw new TypeError('Não é Array') 9 | 10 | let arr = [] 11 | 12 | for (let i=0; i (!head) 2 | ? [] 3 | : [fn(head), ...map(tail, fn)] 4 | 5 | module.exports = map -------------------------------------------------------------------------------- /examples/emojis/emojis.js: -------------------------------------------------------------------------------- 1 | # Não funciona no JS ainda! 2 | 3 | [🍞, 🍗].reduce((sanduba, ingrediente) => 🍔) 4 | 5 | [🍏, 🍎, 🍐, 🍊, 🍋, 🍌] 6 | .filter((fruta) => fruta === 🍎 || fruta === 🍋) 7 | .reduce((suco, fruta) => 🍹) -------------------------------------------------------------------------------- /examples/examples.js: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | Códigos de exemplo do mestre Halan Pinheiro https://github.com/halan 4 | https://gist.github.com/halan/c3c0ec1142b8d1bbf242939c238fbcab 5 | 6 | */ 7 | 8 | const reduce = (reducer, initial, [head, ...tail]) => 9 | head // condition to go or stop 10 | ? reduce(reducer, reducer(initial, head), tail) // recursion 11 | : initial // stop 12 | 13 | const map = (mapper, [head, ...tail]) => 14 | head // condition to go or stop 15 | ? [ mapper(head), ...map(mapper, tail) ] //recursion 16 | : [] // stop 17 | 18 | const filter = (predicate, [head, ...tail]) => 19 | head // condition to go or stop 20 | ? [ ...(predicate(head) ? [head] : []), ...filter(predicate, tail) ] // recursion 21 | : [] // stop 22 | 23 | const zip = ([head, ...tail], [head2, ...tail2]) => 24 | head // condition to go or stop 25 | ? [ [head, head2], ...zip(tail, tail2)] // recursion 26 | : [] // stop 27 | 28 | const find = (predicate, [head, ...tail]) => 29 | head // condition to go or stop 30 | ? predicate(head) ? head : find(predicate, tail) // recursion 31 | : undefined // stop 32 | 33 | const sort = ([head, ...tail]) => // quick sort (https://pt.wikipedia.org/wiki/Quicksort) 34 | head // condition to go or stop 35 | ? [...sort(filter(n => n <= head, tail)), head, ...sort(filter(n => n > head, tail))] // recursion (depends of filter) 36 | : [] // stop 37 | -------------------------------------------------------------------------------- /examples/filter.md: -------------------------------------------------------------------------------- 1 | # Filter 2 | 3 | > Para compreender esse conteúdo leia antes [o `map`](https://github.com/Webschool-io/workshop-js-funcional-free/blob/master/examples/map.md)! 4 | 5 | Como o nome já diz essa função server para filtrar elementos de um *Array* para um novo. O `filter` assim como o `map`, e outros, também é um *Functor* logo não modificará nosso *Array* original. 6 | 7 | Ela funciona da seguinte maneira: 8 | 9 | - recebe 1 elemento do *Array* 10 | - executa a função passada 11 | - caso o retorno seja `true` 12 | - adiciona esse elemento no *Array* de resposta 13 | - caso o retorno seja `false` 14 | - **NÃO** adiciona esse elemento no *Array* de resposta 15 | 16 | Antes de iniciar nosso código vamos escrever o teste para ela usando de base os nossos testes para o `map`: 17 | 18 | ```js 19 | const expect = require('chai').expect 20 | 21 | const filter = require('./../actions/filter') 22 | const value = 2 23 | const values = [1, 2, 3, 4, 5] 24 | const isEven = (value) => !(value % 2) 25 | 26 | describe('Filter', () => { 27 | describe('Número pares', () => { 28 | 29 | const resultadoRecebido = filter(values, isEven) 30 | const resultadoEsperado = [2, 4] 31 | 32 | it('deve retornar um ERRO caso não seja Array', () => { 33 | expect(() => filter(value, isEven)).to.throw(TypeError) 34 | }) 35 | 36 | it('deve retornar um Array', () => { 37 | expect(resultadoRecebido).to.be.an('array') 38 | }) 39 | 40 | it('deve retornar os valores pares', () => { 41 | expect(resultadoRecebido).to.eql(resultadoEsperado) 42 | }) 43 | 44 | it('não deve retornar os valores ímpares', () => { 45 | expect(resultadoRecebido).to.be.not.eql([1, 3, 5]) 46 | }) 47 | }) 48 | }) 49 | ``` 50 | 51 | Agora usando de base nosso `map` inicial teremos que fazer o seguinte: 52 | 53 | - iterar no *Array* 54 | - executar a função passado no elemento atual 55 | - verificar se o retorno dessa função é `true` 56 | - caso seja adicionamos esse elemento no novo *Array* 57 | 58 | Sabendo disso podemos iniciar com o seguinte 59 | código: 60 | 61 | 62 | ```js 63 | const isArrayLike = (value) => !!(value != null 64 | && value != undefined 65 | && value.length 66 | && Array.isArray(value)) 67 | 68 | const filter = (values, fn) => { 69 | 70 | if (!isArrayLike(values)) throw new TypeError('Não é Array') 71 | 72 | let arr = [] 73 | let elemento = undefined 74 | 75 | for (let i=0; i **CARAIIIII! Tamo zica do pântano no baguio hein!** 105 | > 106 | > Mas vamos melhorar. 107 | 108 | No coração do `filter` não precisamos desse `elemento`, pois podemos fazer o seguinte: 109 | 110 | ```js 111 | for (let i=0; i **E PRONTO! Nosso `filter` já está correto. 117 | 118 | Entretanto nosso estudo **mal começou** pois agora iremos partir para a refatoração funcional! 119 | 120 | Vamos começar criando o teste para a função funcional. 121 | 122 | ## Filter - Teste 123 | 124 | Dessa vez como já possuímos um pré-conceito iremos iniciar criando o teste para o `filter`: 125 | 126 | ```js 127 | const expect = require('chai').expect 128 | 129 | const filter = require('./../actions/filter.funcional') 130 | const value = 2 131 | const values = [1, 2, 3, 4, 5] 132 | const isEven = (value) => !(value % 2) 133 | 134 | describe('Filter', () => { 135 | describe('Número pares', () => { 136 | 137 | const resultadoRecebido = filter(isEven, values) 138 | const resultadoEsperado = [2, 4] 139 | 140 | it('deve retornar um ERRO caso não seja Array', () => { 141 | expect(() => filter(isEven, value)).to.throw(TypeError) 142 | }) 143 | 144 | it('deve retornar um Array', () => { 145 | expect(resultadoRecebido).to.be.an('array') 146 | }) 147 | 148 | it('deve retornar os valores pares', () => { 149 | expect(resultadoRecebido).to.eql(resultadoEsperado) 150 | }) 151 | 152 | it('não deve retornar os valores ímpares', () => { 153 | expect(resultadoRecebido).to.be.not.eql([1, 3, 5]) 154 | }) 155 | }) 156 | }) 157 | ``` 158 | 159 | 160 | ## Filter - FUNCIONAL 161 | 162 | O teste criado anteriormente é destinado à essa função: 163 | 164 | ```js 165 | const filter = (predicate, [head, ...tail]) => 166 | head // condition to go or stop 167 | ? [ ...(predicate(head) ? [head] : []), ...filter(predicate, tail) ] // recursion 168 | : [] // stop 169 | ``` 170 | 171 | 172 | Executando nosso teste, `mocha examples/test/filter.funcional.spec.js`, confirmamos sua funcionalidade: 173 | 174 | 175 | ``` 176 | 177 | 178 | Filter 179 | Número pares 180 | ✓ deve retornar um ERRO caso não seja Array 181 | ✓ deve retornar um Array 182 | ✓ deve retornar os valores pares 183 | ✓ não deve retornar os valores ímpares 184 | 185 | 186 | 4 passing (18ms) 187 | 188 | ``` 189 | 190 | Agora vamos fazer aquele teste de mesa maroto para entendermos o passo a passo dela, para isso deixei ela assim: 191 | 192 | ```js 193 | const filter = (predicate, [head, ...tail]) =>{ 194 | console.log('head', head) 195 | console.log('tail', tail) 196 | console.log('[head, ...tail]', [head, ...tail]) 197 | console.log('predicate', predicate) 198 | console.log('predicate(head)', predicate(head)) 199 | console.log('...(predicate(head) ? [head] : [])', ...(predicate(head) ? [head] : [])) 200 | console.log('') 201 | 202 | return head // condition to go or stop 203 | ? [ ...(predicate(head) ? [head] : []), ...filter(predicate, tail) ] // recursion 204 | : [] // stop 205 | } 206 | 207 | module.exports = filter 208 | ``` 209 | 210 | Para conseguirmos o seguinte resultado: 211 | 212 | ```js 213 | head 1 214 | tail [ 2, 3, 4, 5 ] 215 | [head, ...tail] [ 1, 2, 3, 4, 5 ] 216 | predicate (value) => !(value % 2) 217 | predicate(head) false 218 | ...(predicate(head) ? [head] : []) 219 | 220 | head 2 221 | tail [ 3, 4, 5 ] 222 | [head, ...tail] [ 2, 3, 4, 5 ] 223 | predicate (value) => !(value % 2) 224 | predicate(head) true 225 | ...(predicate(head) ? [head] : []) 2 226 | 227 | head 3 228 | tail [ 4, 5 ] 229 | [head, ...tail] [ 3, 4, 5 ] 230 | predicate (value) => !(value % 2) 231 | predicate(head) false 232 | ...(predicate(head) ? [head] : []) 233 | 234 | head 4 235 | tail [ 5 ] 236 | [head, ...tail] [ 4, 5 ] 237 | predicate (value) => !(value % 2) 238 | predicate(head) true 239 | ...(predicate(head) ? [head] : []) 4 240 | 241 | head 5 242 | tail [] 243 | [head, ...tail] [ 5 ] 244 | predicate (value) => !(value % 2) 245 | predicate(head) false 246 | ...(predicate(head) ? [head] : []) 247 | 248 | head undefined 249 | tail [] 250 | [head, ...tail] [ undefined ] 251 | predicate (value) => !(value % 2) 252 | predicate(head) true 253 | ...(predicate(head) ? [head] : []) undefined 254 | ``` 255 | 256 | Vamos analisar apenas as duas primeiras iterações: 257 | 258 | 259 | ```js 260 | head 1 261 | predicate(head) false 262 | ...(predicate(head) ? [head] : []) 263 | 264 | head 2 265 | predicate(head) true 266 | ...(predicate(head) ? [head] : []) 2 267 | ``` 268 | 269 | Percebeu que quando `predicate(head)` retornar `true` o valor de `head` é retornado em `...(predicate(head) ? [head] : [])` que é o *"primeiro/cabeça"* elemento do *Array*, sendo que o segundo é a chamada recursiva como visto abaixo: 270 | 271 | ``` 272 | [ ...(predicate(head) ? [head] : []), ...filter(predicate, tail) ] 273 | ``` 274 | 275 | Graças ao Spread Operator, `...`, podemos retornar um *Array*, `[head] : []` que ele transformará em valores *"comuns"*. Por isso `[2]` vira `2` e `[]` não vira nada, como podemos ver abaixo: 276 | 277 | ```js 278 | > [1, ...[2, 3]] 279 | [ 1, 2, 3 ] 280 | > [1, ...[2, 3], ...[]] 281 | [ 1, 2, 3 ] 282 | ``` 283 | 284 | Isso praticamente mata o entendimento dessa função pois a parte recursiva já vimos [anteriormente com o `map`](https://github.com/Webschool-io/workshop-js-funcional-free/blob/master/examples/map.md). 285 | 286 | 287 | ## Filter - COMPARAÇÃO 288 | 289 | Agora iremos aprender como sair do código imperativo: 290 | 291 | ```js 292 | const isArrayLike = (value) => !!(value != null 293 | && value != undefined 294 | && value.length 295 | && Array.isArray(value)) 296 | 297 | const filter = (values, fn) => { 298 | 299 | if (!isArrayLike(values)) throw new TypeError('Não é Array') 300 | 301 | let arr = [] 302 | 303 | for (let i=0; i 315 | head // condition to go or stop 316 | ? [ ...(predicate(head) ? [head] : []), ...filter(predicate, tail) ] // recursion 317 | : [] // stop 318 | 319 | ``` 320 | 321 | Antes de tudo obviamente precisamos criar o nosso teste para a função que iremos criar/refatorar, que na verdade é o mesmo criado lá no início mudando apenas a chamada do módulo do `filter`: 322 | 323 | ```js 324 | const expect = require('chai').expect 325 | 326 | const filter = require('./../actions/filter.nosso') 327 | const value = 2 328 | const values = [1, 2, 3, 4, 5] 329 | const isEven = (value) => !(value % 2) 330 | 331 | describe('Filter', () => { 332 | describe('Número pares', () => { 333 | 334 | const resultadoRecebido = filter(values, isEven) 335 | const resultadoEsperado = [2, 4] 336 | 337 | it('deve retornar um ERRO caso não seja Array', () => { 338 | expect(() => filter(value, isEven)).to.throw(TypeError) 339 | }) 340 | 341 | it('deve retornar um Array', () => { 342 | expect(resultadoRecebido).to.be.an('array') 343 | }) 344 | 345 | it('deve retornar os valores pares', () => { 346 | expect(resultadoRecebido).to.eql(resultadoEsperado) 347 | }) 348 | 349 | it('não deve retornar os valores ímpares', () => { 350 | expect(resultadoRecebido).to.be.not.eql([1, 3, 5]) 351 | }) 352 | }) 353 | }) 354 | ``` 355 | 356 | Como já sabemos precisamos refatorar os parâmetros da nossa função, a condição de parada da sua recursão e a própria chamada da recursão, então vamos por partes: 357 | 358 | ```js 359 | const filter = ([head, ...tail], fn) => { 360 | 361 | let arr = [] 362 | 363 | if (!head) return arr 364 | 365 | return arr 366 | } 367 | ``` 368 | 369 | Mudamos os parâmetros para `[head, ...tail], fn` e nossa condição de saída é: `if (!head)`. Porém isso aí ainda não faz muita coisa como podemos ver a execução do nosso teste: 370 | 371 | ``` 372 | 373 | 374 | Filter 375 | Número pares 376 | ✓ deve retornar um ERRO caso não seja Array 377 | ✓ deve retornar um Array 378 | 1) deve retornar os valores pares 379 | ✓ não deve retornar os valores ímpares 380 | 381 | 382 | 3 passing (22ms) 383 | 1 failing 384 | 385 | 1) Filter Número pares deve retornar os valores pares: 386 | 387 | AssertionError: expected [] to deeply equal [ 2, 4 ] 388 | + expected - actual 389 | 390 | -[] 391 | +[ 392 | + 2 393 | + 4 394 | +] 395 | 396 | at Context.it (examples/test/filter.nosso.spec.js:23:36) 397 | 398 | 399 | ``` 400 | 401 | Continuando, precisamos selecionar o elemento quando o retorno da `fn` for `true` e depois fazer a chamada recursiva, basicamente estamos aqui: `[ ...(predicate(head) ? [head] : []), ...filter(predicate, tail) ]`. 402 | 403 | **Então bora refatorar nosso código:** 404 | 405 | ```js 406 | const filter = ([head, ...tail], fn) => { 407 | 408 | let arr = [] 409 | let elemento = [] 410 | 411 | if (!head) return arr 412 | if (fn(head)) elemento = [head] 413 | 414 | return [...elemento, ...filter(tail, fn)] 415 | } 416 | 417 | module.exports = filter 418 | ``` 419 | 420 | Agora para verificarmos se já acertamos basta executar o teste e ver: 421 | 422 | ``` 423 | 424 | Filter 425 | Número pares 426 | ✓ deve retornar um ERRO caso não seja Array 427 | ✓ deve retornar um Array 428 | ✓ deve retornar os valores pares 429 | ✓ não deve retornar os valores ímpares 430 | 431 | 432 | 4 passing (17ms) 433 | 434 | 435 | ``` 436 | 437 | Porém obviamente ainda podemos melhora-la e é isso que faremos transformando esse `if` em um ternário dentro do *Array* do nosso `return`: 438 | 439 | 440 | ```js 441 | const filter = ([head, ...tail], fn) => { 442 | 443 | let elemento = [] 444 | 445 | if (!head) return [] 446 | 447 | return [ ...(fn(head) ? [head] : []), ...filter(tail, fn)] 448 | } 449 | ``` 450 | 451 | > **Agora para finalizar com chave de ouro hein!** 452 | > 453 | > Vamos colocar toda essa função em 1 linha! 454 | 455 | ```js 456 | const filter = ([head, ...tail], fn) => (!head) 457 | ? [] 458 | : [ ...(fn(head) ? [head] : []), 459 | ...filter(tail, fn)] 460 | ``` 461 | 462 | Ou invertendo a lógica para deixarmos igual a função em que nos baseamos: 463 | 464 | 465 | ```js 466 | const filter = ([head, ...tail], fn) => (head) 467 | ? [ ...(fn(head) ? [head] : []), 468 | ...filter(tail, fn)] 469 | : [] 470 | 471 | ``` 472 | 473 | Passando lindamente no nosso teste: 474 | 475 | ``` 476 | 477 | 478 | Filter 479 | Número pares 480 | ✓ deve retornar um ERRO caso não seja Array 481 | ✓ deve retornar um Array 482 | ✓ deve retornar os valores pares 483 | ✓ não deve retornar os valores ímpares 484 | 485 | 486 | 4 passing (17ms) 487 | 488 | ``` 489 | -------------------------------------------------------------------------------- /examples/map.md: -------------------------------------------------------------------------------- 1 | # Map 2 | 3 | Lembrando do [nosso material do workshop](https://github.com/Webschool-io/workshop-js-funcional-free#map) sobre `map`: 4 | 5 | > O `map` é um método que executa um **`callback`** para cada valor de um **`array`** modificando os mesmos, isso faz com que o **`map`** crie um novo **`array`** com os novos valores obtidos. Exemplo: 6 | 7 | 8 | ```javascript 9 | var x = [1,2,3].map(function (value) { 10 | return value * 2 11 | }); 12 | 13 | console.log(x) //[2,4,6] 14 | ``` 15 | 16 | Em ES6: 17 | 18 | ```javascript 19 | const x = [1,2,3].map(v => v * 2); 20 | console.log(x) //[2,4,6] 21 | ``` 22 | 23 | [Documentação oficial do map em JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) 24 | 25 | Antes de entendermos como implementar o `map`, precisamos [lembrar que ele é um Functor](https://github.com/Webschool-io/workshop-js-funcional-free#functor): 26 | 27 | 28 | > A Functor is a function, given a value and a function, unwraps the values to get to its inner value(s), calls the given function with the inner value(s), wraps the returned values in a new structure, and returns the new structure. 29 | 30 | Vamos entender parte por parte: 31 | 32 | - *Functor* é uma função que irá receber um valor e uma função; 33 | - Desencapsula[?] os valores para chegar a seu(s) valor(es) interno(s); 34 | - Chama a função repassada com o(s) valor(es) interno(s); 35 | - Encapsula os valores devolvidos em uma nova estrutura; 36 | - e retorna a nova estrutura. 37 | 38 | Sabendo disso podemos começar a montar nosso `map`, criando a função `functor` que receberá o valor e a função a ser executada: 39 | 40 | ```js 41 | function functor(value, fn) { 42 | return fn(value) 43 | }; 44 | 45 | ``` 46 | 47 | Colocando ela como um módulo para ser mais facilmente testada: 48 | 49 | ```js 50 | const map = (value, fn) => fn(value) 51 | 52 | module.exports = map 53 | ``` 54 | 55 | Podemos testar ela fazendo o seguinte: 56 | 57 | ```js 58 | function plus10(value) { 59 | return value + 10 60 | }; 61 | 62 | let p10 = functor(10, plus10) 63 | 64 | console.log(p10) 65 | ``` 66 | 67 | ## Map - Teste 68 | 69 | Com isso já criamos a base para o nosso `map`, agora obviamente precisamos fazer a mesma funcionar com *Array*, porém antes iremos escrever o **TESTE** para ela: 70 | 71 | ```js 72 | const expect = require('chai').expect 73 | 74 | const map = require('./../actions/map') 75 | const value = 2 76 | const values = [1, 2, 3, 4, 5] 77 | const times10 = (value) => value * 10 78 | 79 | describe('Map', () => { 80 | 81 | describe('Number', () => { 82 | 83 | const resultadoRecebido = map(value, times10) 84 | const resultadoEsperado = 20 85 | 86 | it('deve retornar um Number', () => { 87 | expect(resultadoRecebido).to.be.a('number') 88 | }) 89 | 90 | it('deve retornar o valor antigo ${value} multiplicado por 10', () => { 91 | expect(resultadoRecebido).to.eql(resultadoEsperado) 92 | }) 93 | }) 94 | 95 | describe('Array', () => { 96 | 97 | const resultadoRecebido = map(values, times10) 98 | const resultadoEsperado = [10, 20, 30, 40, 50] 99 | 100 | it('deve retornar um Array', () => { 101 | expect(resultadoRecebido).to.be.an('array') 102 | }) 103 | 104 | it('deve retornar os valor antigos multiplicados por 10', () => { 105 | expect(resultadoRecebido).to.eql(resultadoEsperado) 106 | }) 107 | }) 108 | }) 109 | ``` 110 | 111 | Depois basta executarmos `mocha examples/test/map.spec.js`: 112 | 113 | ``` 114 | ➜ mocha examples/test/map.spec.js 115 | 116 | 117 | Map 118 | Number 119 | ✓ deve retornar um Number 120 | ✓ deve retornar o valor antigo ${value} multiplicado por 10 121 | Array 122 | 1) deve retornar um Array 123 | 2) deve retornar os valor antigos multiplicados por 10 124 | 125 | 126 | 2 passing (21ms) 127 | 2 failing 128 | 129 | 1) Map Array deve retornar um Array: 130 | AssertionError: expected NaN to be an array 131 | at Context.it (examples/test/map.spec.js:30:39) 132 | 133 | 2) Map Array deve retornar os valor antigos multiplicados por 10: 134 | AssertionError: expected NaN to deeply equal [ 10, 20, 30, 40, 50 ] 135 | at Context.it (examples/test/map.spec.js:34:36) 136 | 137 | 138 | ``` 139 | 140 | Criei o teste com o *Number* apenas para vermos como a função funciona com 1 valor porém quebra com 1 *Array* e o `map` na verdade só deveria funcionar com *Arrays*, **então bora refatorar!** 141 | 142 | Inicialmente irei apenas retornar um *Array* para passarmos no teste do tipo de retorno, aliás também **comentei o teste do *Number* para nos focarmos apenas no *Array*:** 143 | 144 | ```js 145 | const map = (values, fn) => { 146 | let arr = [] 147 | 148 | return arr 149 | } 150 | ``` 151 | 152 | Para depois executarmos o teste novamente: 153 | 154 | ``` 155 | Map 156 | Array 157 | ✓ deve retornar um Array 158 | 1) deve retornar os valor antigos multiplicados por 10 159 | 160 | 161 | 1 passing (21ms) 162 | 1 failing 163 | 164 | 1) Map Array deve retornar os valor antigos multiplicados por 10: 165 | 166 | AssertionError: expected [] to deeply equal [ 10, 20, 30, 40, 50 ] 167 | + expected - actual 168 | 169 | -[] 170 | +[ 171 | + 10 172 | + 20 173 | + 30 174 | + 40 175 | + 50 176 | +] 177 | 178 | at Context.it (examples/test/map.spec.js:34:36) 179 | ``` 180 | 181 | Agora só falta passarmos pelo segundo teste, esse sim é nossa verdadeira prova, então vamos pensar: 182 | 183 | > Preciso fazer a função executar a função `fn` passada para cada elemento do *Array*, porém se estamos fazendo o `map` logicamente não podemos utilizar o `forEach` logo usaremos o `for`! 184 | 185 | 186 | Sabendo disso nosso código ficará assim: 187 | 188 | ```js 189 | const map = (values, fn) => { 190 | let arr = [] 191 | 192 | for (let i=0; i **SHOW DE BOLA!!!** Agora só precisamos implementar um teste para verificar se a entrada é realmente um *Array*. 215 | 216 | Basta adicionarmos esse teste no nosso `describe`: 217 | 218 | 219 | ```js 220 | it('deve retornar um ERRO caso não seja Array', () => { 221 | expect(() => map(2, times10)).to.throw(TypeError) 222 | }) 223 | 224 | ``` 225 | 226 | Agora devemos executar o teste e vê-lo falhar para depois refatorarmos nossa função `map`: 227 | 228 | 229 | ``` 230 | 231 | Map 232 | Array 233 | 1) deve retornar um ERRO caso não seja Array 234 | ✓ deve retornar um Array 235 | ✓ deve retornar os valor antigos multiplicados por 10 236 | 237 | 238 | 2 passing (22ms) 239 | 1 failing 240 | 241 | 1) Map Array deve retornar um ERRO caso não seja Array: 242 | AssertionError: expected [Function] to throw TypeError 243 | at Context.it (examples/test/map.spec.js:31:45) 244 | 245 | 246 | ``` 247 | 248 | E para fazeros essa validação, se é um *Array*, existem diversas formas, contudo utilizaremos a mais fácil: `Array.isArray(value)`. 249 | 250 | Deixando nosso código assim: 251 | 252 | ```js 253 | const isArrayLike = (value) => !!(value != null 254 | && value != undefined 255 | && value.length 256 | && Array.isArray(value)) 257 | 258 | const map = (values, fn) => { 259 | 260 | if (!isArrayLike(values)) throw new TypeError('Não é Array') 261 | 262 | let arr = [] 263 | 264 | for (let i=0; i Percebeu uma coisa diferente na função `isArrayLike`? 275 | > 276 | > **SIM! As duas `!`(exclamações).** 277 | > 278 | > Por que você acha que fiz isso? 279 | > 280 | > **Bem simples, fiz isso para forçar o retorno de um BOOLEANO.** Pois quando utilizamos o primeiro `!` ele irá **NEGAR** o valor e usando o segundo `!` ele irá **NEGAR O VALOR DA NEGAÇÃO ANTERIOR** logo transformando esse valor para o que desejamos. 281 | > 282 | > **Falei que era simples!** 283 | 284 | 285 | ## Map FUNCIONAL 286 | 287 | Agora que já temos nossa implementação precisamos analisar esse exemplo puramente funcional e entender como refatorar nosso código até chegar nele: 288 | 289 | ```js 290 | // map.funcional.js 291 | const map = (mapper, [head, ...tail]) => 292 | head // condition to go or stop 293 | ? [ mapper(head), ...map(mapper, tail) ] //recursion 294 | : [] // stop 295 | ``` 296 | 297 | Como você deve ter percebido a ordem dos parâmetros é invertida, por isso iremos criar um outro teste para essa função nova apenas invertendo os parâmetros na chamada da função: 298 | 299 | ```js 300 | const expect = require('chai').expect 301 | 302 | const map = require('./../actions/map.funcional') 303 | const value = 2 304 | const values = [1, 2, 3, 4, 5] 305 | const times10 = (value) => value * 10 306 | 307 | describe('Map', () => { 308 | describe('Array', () => { 309 | 310 | const resultadoRecebido = map(times10, values) 311 | const resultadoEsperado = [10, 20, 30, 40, 50] 312 | 313 | it('deve retornar um ERRO caso não seja Array', () => { 314 | expect(() => map(times10, value)).to.throw(TypeError) 315 | }) 316 | 317 | it('deve retornar um Array', () => { 318 | expect(resultadoRecebido).to.be.an('array') 319 | }) 320 | 321 | it('deve retornar os valor antigos multiplicados por 10', () => { 322 | expect(resultadoRecebido).to.eql(resultadoEsperado) 323 | }) 324 | }) 325 | }) 326 | ``` 327 | 328 | E depois rodamos com `mocha examples/test/map.funcional.spec.js`: 329 | 330 | ``` 331 | 332 | Map 333 | Array 334 | ✓ deve retornar um ERRO caso não seja Array 335 | ✓ deve retornar um Array 336 | ✓ deve retornar os valor antigos multiplicados por 10 337 | 338 | 339 | 3 passing (16ms) 340 | ``` 341 | 342 | > **PERFEITO!** Nosso teste está passando, agora vamos para a análise pesada do bagulho. 343 | 344 | 345 | ```js 346 | // map.funcional.js 347 | const map = (mapper, [head, ...tail]) => 348 | head // condition to go or stop 349 | ? [ mapper(head), ...map(mapper, tail) ] //recursion 350 | : [] // stop 351 | ``` 352 | 353 | Analisamos seus parâmetros vimos que o primeiro é a função a ser executada em cada posição do *Array* mas e esse segundo parâmetro `[head, ...tail]`? 354 | 355 | Vamos criar um tipo de teste de mesa para que possamos entender os valores de entrada, para isso modifiquei a função para: 356 | 357 | ```js 358 | const map = (mapper, [head, ...tail]) =>{ 359 | console.log('mapper', mapper) 360 | console.log('head', head) 361 | console.log('tail', tail) 362 | console.log('[head, ...tail]', [head, ...tail]) 363 | 364 | return head // condition to go or stop 365 | ? [ mapper(head), ...map(mapper, tail) ] //recursion 366 | : [] // stop 367 | } 368 | ``` 369 | 370 | Executado o nosso teste podemos verificar o seguinte (peguei apenas a parte do console.log): 371 | 372 | ``` 373 | mapper (value) => value * 10 374 | head 1 375 | tail [ 2, 3, 4, 5 ] 376 | [head, ...tail] [ 1, 2, 3, 4, 5 ] 377 | 378 | mapper (value) => value * 10 379 | head 2 380 | tail [ 3, 4, 5 ] 381 | [head, ...tail] [ 2, 3, 4, 5 ] 382 | 383 | mapper (value) => value * 10 384 | head 3 385 | tail [ 4, 5 ] 386 | [head, ...tail] [ 3, 4, 5 ] 387 | 388 | mapper (value) => value * 10 389 | head 4 390 | tail [ 5 ] 391 | [head, ...tail] [ 4, 5 ] 392 | 393 | mapper (value) => value * 10 394 | head 5 395 | tail [] 396 | [head, ...tail] [ 5 ] 397 | 398 | mapper (value) => value * 10 399 | head undefined 400 | tail [] 401 | [head, ...tail] [ undefined ] 402 | ``` 403 | 404 | Analisando apenas a primeira parte: 405 | 406 | ```js 407 | mapper (value) => value * 10 408 | head 1 409 | tail [ 2, 3, 4, 5 ] 410 | [head, ...tail] [ 1, 2, 3, 4, 5 ] 411 | ``` 412 | 413 | Podemos notar que `[head, ...tail]` nada mais é do que o *Array* completo que entra no `map`, onde o `head` é a primeira posição e o `tail` é o resto. 414 | 415 | Note que passamos apenas 1 *Array* pra função `map`, porém ela separa em 2 valores: `head` e `tail`. 416 | 417 | Isso acontece graças a [destructuring assignment](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Operators/Atribuicao_via_desestruturacao) que é a atribuição via desestruturação, ou seja, como o próprio nome diz: ela desestrutura um Objeto ou *Array* para variáveis definidas. 418 | 419 | **Nesse caso a primeira posição do *Array* vai para `head` e o resto para `tail`!** Mas como ela sabe que o resto vai para o `tail?` 420 | 421 | > Graças a essa chamada `...tail` e sabe o porquê foi utilizado esses `...`? 422 | > 423 | > Essa funcionalidade chama-se [Spread operator](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Operators/Spread_operator) e sua descrição é: 424 | > 425 | > "O operador spread permite uma expressão ser expandida em locais onde múltiplo argumentos (por chamadas de função) ou múltiplos elementos (por array literais) são esperados." 426 | 427 | 428 | Deixando isso mais claro ainda com esse exemplo executado no *Terminal* (precisa executar `node` antes!): 429 | 430 | ```js 431 | > let tail = [ 2, 3, 4, 5 ] 432 | > [666, ...tail] 433 | [666, 2, 3, 4, 5 ] 434 | > const f = ([a, b, resto]) => console.log(a, b, resto) 435 | undefined 436 | > f([1, 2, 3, 4, 5]) 437 | 1 2 3 438 | undefined 439 | > const g = ([a, b, ...resto]) => console.log(a, b, resto) 440 | undefined 441 | > g([1, 2, 3, 4, 5]) 442 | 1 2 [ 3, 4, 5 ] 443 | ``` 444 | 445 | Logo ele irá definindo cada valor na sequência do *Array* e para pegarmos "todos" os que sobraram basta usar o *Spread Operator*! 446 | 447 | Depois de entendermos com quais valores estamos trabalhando precisamos entender a estrutura da nossa função: 448 | 449 | ```js 450 | head // condition to go or stop 451 | ? [ mapper(head), ...map(mapper, tail) ] //recursion 452 | : [] // stop 453 | ``` 454 | 455 | Nesse caso ela está usando um `if` ternário, traduzindo para um `if` normal fica assim: 456 | 457 | ```js 458 | if (head) { 459 | return [ mapper(head), ...map(mapper, tail) ] 460 | } else { 461 | return [] 462 | } 463 | ``` 464 | 465 | > Perceba que ele não precisou usar o `return`, isso se deve pela forma da criação da função `map` que utilizou [Arrow Function](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Functions/Arrow_functions), `() =>`, quando ela possuir apenas 1 linha a qual já faz o retorno da função não é necessário utilizar `{ }` muito menos o `return`. 466 | 467 | 468 | Porém além disso ela também está usando recursividade como visto aqui: `[ mapper(head), ...map(mapper, tail) ]`. 469 | 470 | Agora que eu lhe pergunto: 471 | 472 | > O que está acontecendo nessa linha? 473 | > 474 | > Vamos novamente analisar, uma parte do, nosso teste de mesa. 475 | 476 | 477 | ``` 478 | mapper (value) => value * 10 479 | head 1 480 | tail [ 2, 3, 4, 5 ] 481 | [head, ...tail] [ 1, 2, 3, 4, 5 ] 482 | 483 | mapper (value) => value * 10 484 | head 2 485 | tail [ 3, 4, 5 ] 486 | [head, ...tail] [ 2, 3, 4, 5 ] 487 | 488 | mapper (value) => value * 10 489 | head 3 490 | tail [ 4, 5 ] 491 | [head, ...tail] [ 3, 4, 5 ] 492 | ``` 493 | 494 | Sabemos então que na primeira parte essa linha `[ mapper(head), ...map(mapper, tail) ]` irá executar/retornar: 495 | 496 | ```js 497 | [ 1 * 10, // mapper(head) 498 | ...map((n) => n * 10, [ 2, 3, 4, 5 ])// ...map(mapper, tail) 499 | ] 500 | ``` 501 | 502 | Vamos nos atentar ao segundo valor desse *Array* que acredito ser o mais "complexo" de toda essa função. Quando chamarmos `map(mapper, tail)` ele irá chamar a mesma função onde estamos, `map`, passando agora apenas o *Array* `tail` que já não possui o primeiro valor, `head`, e retornar um *Array*. **Porém como precisamos pegar cada valor desse *Array* e "juntar" com o primeiro valor já passado anteriormente necessitamos usar `...map(mapper, tail)` pois será dessa forma que iremos criar o `[ mapper(head), ...map(mapper, tail) ]`.** 503 | 504 | Então para criar a primeira iteração dessa função ela se chama para poder criar o resto do *Array*, entretando você deve se perguntar: 505 | 506 | > E como é que ela sabe que deve parar de se chamar? 507 | > 508 | > **ÓTIMA PERGUNTA!** Analise aqui comigo as duas últimas iterações do nosso teste de mesa: 509 | 510 | ```js 511 | head 5 512 | tail [] 513 | [head, ...tail] [ 5 ] 514 | 515 | head undefined 516 | tail [] 517 | [head, ...tail] [ undefined ] 518 | ``` 519 | 520 | Sabemos através da "tradução" do `if` ternário para o comum que o teste lógico é em cima do `head`, ou seja, enquanto existir valor pro `head` ela irá continuar executando. Logo se esse valor for `undefined` ela irá parar de se chamar e irá retornar `[]`. 521 | 522 | Então perceba que o *Array* de retorno da função `map` é gerado dinamicamente em cima do mesmo *Array*, note que o valor passado para cada iteração dessa recursividade é o `[head, ...tail]`. Então vamos ver como ele se comporta no nosso teste de mesa: 523 | 524 | 525 | ```js 526 | [head, ...tail] [ 1, 2, 3, 4, 5 ] 527 | 528 | [head, ...tail] [ 2, 3, 4, 5 ] 529 | 530 | [head, ...tail] [ 3, 4, 5 ] 531 | 532 | [head, ...tail] [ 4, 5 ] 533 | 534 | [head, ...tail] [ 5 ] 535 | 536 | [head, ...tail] [ undefined ] 537 | ``` 538 | 539 | > Sabe o porquê esse *Array* vai diminuindo? 540 | > 541 | > 542 | > 543 | > 544 | > **EXATAMENTE!** 545 | > 546 | > Porque a cada iteração nós retiramos a primeira posição que é o `head` e aplicamos a função `mapper` apenas nesse valor, isso vai acontecendo até que não exista mais elementos a serem processados. 547 | 548 | 549 | ## Map - COMPARAÇÃO 550 | 551 | Agora iremos aprender como sair do código imperativo: 552 | 553 | ```js 554 | const isArrayLike = (value) => !!(value != null 555 | && value != undefined 556 | && value.length 557 | && Array.isArray(value)) 558 | 559 | const map = (values, fn) => { 560 | 561 | if (!isArrayLike(values)) throw new TypeError('Não é Array') 562 | 563 | let arr = [] 564 | 565 | for (let i=0; i 577 | head // condition to go or stop 578 | ? [ mapper(head), ...map(mapper, tail) ] //recursion 579 | : [] // stop 580 | ``` 581 | 582 | Primeira coisa que devemos fazer é refatorar nossos parâmetros de entrada e **uma coisa impotantíssima: a condição de parada da função recursiva** 583 | 584 | ```js 585 | const map = ([head, ...tail], fn) => { 586 | 587 | let arr = [] 588 | 589 | if (!head) return arr 590 | 591 | } 592 | ``` 593 | 594 | Depois precisamos fazer a chamada recursiva passando os valores corretamente: 595 | 596 | ```js 597 | const map = ([head, ...tail], fn) => { 598 | 599 | if (!head) return [] 600 | 601 | return [fn(head), map(tail, fn)] 602 | } 603 | ``` 604 | 605 | Com isso estamos quase lá, porém eu não usei `...map(tail, fn)` apenas para vermos como ficaria essa saída errada: 606 | 607 | ```js 608 | 609 | - [ 610 | - 20 611 | - [ 612 | - 30 613 | - [ 614 | - 40 615 | - [ 616 | - 50 617 | - [] 618 | - ] 619 | - ] 620 | - ] 621 | - ] 622 | ``` 623 | 624 | > **Percebeu como o `...` é importantíssimo?** 625 | 626 | Então refatorando deixaremos nossa função assim: 627 | 628 | 629 | ```js 630 | const map = ([head, ...tail], fn) => { 631 | 632 | if (!head) return [] 633 | 634 | return [fn(head), ...map(tail, fn)] 635 | } 636 | ``` 637 | 638 | Agora basta refatorarmos esse `if` normal para um ternário: 639 | 640 | ```js 641 | const map = ([head, ...tail], fn) => (!head) 642 | ? [] 643 | : [fn(head), ...map(tail, fn)] 644 | 645 | module.exports = map 646 | ``` 647 | 648 | Executando, `mocha examples/test/map.nosso.spec.js`, nosso teste para essa função teremos o seguinte resultado: 649 | 650 | ``` 651 | 652 | Map 653 | Array 654 | ✓ deve retornar um ERRO caso não seja Array 655 | ✓ deve retornar um Array 656 | ✓ deve retornar os valor antigos multiplicados por 10 657 | 658 | 659 | 3 passing (17ms) 660 | ``` 661 | 662 | ## Map - Conclusão 663 | 664 | Para criarmos aprendermos essa função foi necessário utilizarmos/conhecermos: 665 | 666 | - arrow function 667 | - destructuring assignment 668 | - spread operator 669 | - if ternário 670 | - recursão 671 | 672 | -------------------------------------------------------------------------------- /examples/reduce.md: -------------------------------------------------------------------------------- 1 | # Reduce 2 | 3 | [Função reduce OFICIAL](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce) 4 | 5 | ## Reduce - FUNCIONAL 6 | 7 | ```js 8 | const reduce = (reducer, initial, [head, ...tail]) => 9 | head // condition to go or stop 10 | ? reduce(reducer, reducer(initial, head), tail) // recursion 11 | : initial // stop 12 | ``` -------------------------------------------------------------------------------- /examples/refactors/README.md: -------------------------------------------------------------------------------- 1 | # Refatoração do coração <3 2 | 3 | Hoje estava de bobeira no grupo [angularjsbrasil](https://telegram.me/angularjsbrasil) quando me deparo com esse código: 4 | 5 | ```js 6 | module("app") 7 | .filter('searchById', function () { 8 | return function (array, id) { 9 | if (array) { 10 | var i = 0; 11 | var len = array.length; 12 | for (; i < len; i++) { 13 | if (+array[i].id == +id) { 14 | return array[i]; 15 | } 16 | } 17 | return null; 18 | } 19 | } 20 | }); 21 | ``` 22 | 23 | **Logo minha mente deu um estalo e precisei refatorar deixando ele desse jeito:** 24 | 25 | ```js 26 | module("app") 27 | .filter('searchById', () => (array, id) => 28 | (array) 29 | ? array.filter(el => el.id == id) 30 | : null 31 | ); 32 | ``` 33 | 34 | > Então o que aconteceu ali? 35 | 36 | Primeiramente troquei de *function* para *arrow function*: 37 | 38 | 39 | ```js 40 | module("app") 41 | .filter('searchById', () => { 42 | return (array, id) => { 43 | if (array) { 44 | var i = 0; 45 | var len = array.length; 46 | for (; i < len; i++) { 47 | if (+array[i].id == +id) { 48 | return array[i]; 49 | } 50 | } 51 | return null; 52 | } 53 | } 54 | }); 55 | ``` 56 | 57 | Nem precisamos corrigir o `for` pois usarei o `filter` tendo em vista que é isso que ele gostaria que acontecesse, vide esse código: 58 | 59 | ```js 60 | if (+array[i].id == +id) { 61 | return array[i]; 62 | } 63 | ``` 64 | 65 | Continuando: 66 | 67 | ```js 68 | module("app") 69 | .filter('searchById', () => { 70 | return (array, id) => { 71 | if (array) { 72 | retun array.filter(el => parseInt(el.id) === parseInt(id)) 73 | } 74 | } 75 | }); 76 | ``` 77 | 78 | Vamos dar aquele toque maroto para retirar os `return`s: 79 | 80 | 81 | ```js 82 | module("app") 83 | .filter('searchById', () => (array, id) => { 84 | if (array) { 85 | retun array.filter(el => parseInt(el.id) === parseInt(id)) 86 | } 87 | }); 88 | ``` 89 | 90 | E para finalizar trocamos o `if` por um ternário: 91 | 92 | ```js 93 | module("app") 94 | .filter('searchById', () => (array, id) => 95 | array 96 | ? array.filter(el => el.id == id) 97 | : null 98 | ); 99 | ``` 100 | 101 | ## Finalizando 102 | 103 | Porém o nosso querido mestre do Funcional, [Halan](https://github.com/halan), conseguiu melhorar mais. Esse cara é foda! 104 | 105 | Encapsulando em uma função para deixarmos mais legível: 106 | 107 | ```js 108 | const idFilter = (array, id) => 109 | array 110 | ? array.filter(el => el.id == id) 111 | : null 112 | 113 | module("app").filter('searchById', () => idFilter) 114 | ``` 115 | 116 | > E seu te disser que ainda rola melhorar mais um pouco? 117 | 118 | 119 | Pois sim! O Cleiton Loyola lá no grupo [Programação Funcional Brasil](https://telegram.me/ProgramacaoFuncionalBrasil) melhorou eliminando a nossa necessidade do `if` ternário: 120 | 121 | ```js 122 | const idFilter = (array, id) => 123 | (array || []).filter(el => el.id == id) 124 | 125 | module("app").filter('searchById', () => idFilter) 126 | ``` 127 | 128 | Dessa forma é bem melhor pois não estamos retornando `null` fazendo com que nossa função realmente fique funcional! 129 | 130 | Sobre não retornar o `null` achei essa explicação a mais simples possível: 131 | 132 | > In Clean Code by Robert Martin he writes that returning null is bad design when you can instead return, say, empty array. Since expected result is an array, why not? It'll enable you to iterate over result without any extra conditions. If it's an integer, maybe 0 will suffice, if it's a hash, empty hash. etc. 133 | > 134 | > The premise is to not force calling code to immediately handle issues. Calling code may not want to concern itself with them. That's also why in many cases exceptions is better than nil. 135 | 136 | ![](https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/master/assets/images/refatoracao01.png) 137 | 138 | > **Espero que esse meu hobby de refatorar o código dos outros ajude alguém.** 139 | 140 | ## Referências 141 | 142 | - [Is returning null bad design?](http://stackoverflow.com/questions/1274792/is-returning-null-bad-design) 143 | - [Why NULL is Bad?](http://www.yegor256.com/2014/05/13/why-null-is-bad.html) 144 | - [Is returning null bad design?](http://stackoverflow.com/questions/1274792/is-returning-null-bad-design) 145 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /examples/subway/README.md: -------------------------------------------------------------------------------- 1 | # Fazendo um sanduba no Subway 2 | 3 | Depois de já termos criado manualmente as funções de `map`, `filter` e `reduce` chegou a hora de utiliza-las em algo do mundo real e **nada melhor do que unir programação com larica!** 4 | 5 | > Imagine que você está programando um robô para fazer sanduíches no Subway. 6 | 7 | ![](https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/master/assets/images/robo.jpg) 8 | 9 | Logo deveremos ter de antemão o menu: 10 | 11 | ```js 12 | const paes = ['Integral', 13 | '9 grãos', 14 | '9 grãos com aveia e mel', 15 | 'Italiano', 16 | 'Parmesão e orégano', 17 | 'Três Queijos'] 18 | const tamanhos = [15, 30] 19 | const recheios = ['Vegetariano', 'Atum', 'Italiano', 'Frango'] 20 | const queijos = ['Cheddar', 'Prato', 'Suíço'] 21 | const saladas = ['Alface', 22 | 'Tomates', 23 | 'Pepinos', 24 | 'Pimentões verdes', 25 | 'Cebolas', 26 | 'Azeitonas pretas', 27 | 'Picles'] 28 | const molhos = ['Mostarda e Mel', 29 | 'Cebola Agridoce', 30 | 'Barbecue', 31 | 'Parmesão', 32 | 'Chipotle', 33 | 'Mostarda', 34 | 'Maionese'] 35 | 36 | const menu = { paes, tamanhos, recheios, queijos, saladas, molhos } 37 | ``` 38 | 39 | Agora o robô está pronto para receber seu primeiro cliente, esse chega com o seguinte pedido: 40 | 41 | > Integral 15, Atum, queijo Suíço. 42 | > 43 | > Sim, quente. 44 | > 45 | > (30 segundos depois) 46 | > 47 | > Alface, Cebolas, Azeitonas pretas, Picles 48 | > 49 | > Mostarda e Mel, Cebola Agridoce e Chipotle 50 | > 51 | > Pode feixar. 52 | 53 | Traduzindo o pedido para JSON teremos: 54 | 55 | ```js 56 | const pedido = { 57 | pao: ['Integral'], 58 | tamanho: [15], 59 | recheio: ['Atum'], 60 | queijo: ['Suíço'], 61 | quente: true, 62 | saladas: ['Alface', 63 | 'Cebolas', 64 | 'Azeitonas pretas', 65 | 'Picles' 66 | ], 67 | molhos: ['Mostarda e Mel', 68 | 'Cebola Agridoce', 69 | 'Chipotle' 70 | ] 71 | } 72 | ``` 73 | 74 | Para que o robo possa montar o sanduíche precisamos definir suas ações: 75 | 76 | ```js 77 | const montaSanduba = (sanduba) => { 78 | 79 | const escolhaPao = (pao) => sanduba.pao.includes(pao) 80 | const escolhaTamanho = (tamanho) => sanduba.tamanho.includes(tamanho) 81 | const escolhaRecheio = (recheio) => sanduba.recheio.includes(recheio) 82 | const escolhaQueijo = (queijo) => sanduba.queijo.includes(queijo) 83 | const escolhaSaladas = (salada) => sanduba.saladas.includes(salada) 84 | const escolhaMolhos = (molho) => sanduba.molhos.includes(molho) 85 | const fechaSanduba = (sanduba, ingrediente) => sanduba + '\r\n' + ingrediente 86 | const esquentar = (ingrediente, i) => ( i < 3 && 87 | !Number.isInteger(ingrediente) && 88 | sanduba.quente) 89 | ? ingrediente + '(quente)' 90 | : ingrediente 91 | 92 | ``` 93 | 94 | Ainda dentro da função `montaSanduba` iremos então executar as ações previamente definidas: 95 | 96 | ```js 97 | return '\r\nSanduba fechado com: ' + 98 | [ menu.paes.filter(escolhaPao) + ' ' 99 | + menu.tamanhos.filter(escolhaTamanho), 100 | ...menu.recheios.filter(escolhaRecheio), 101 | ...menu.queijos.filter(escolhaQueijo) 102 | ] 103 | .map(esquentar) 104 | .concat([ 105 | ...menu.saladas.filter(escolhaSaladas), 106 | ...menu.molhos.filter(escolhaMolhos) 107 | ]) 108 | .reduce(fechaSanduba, '') 109 | } 110 | 111 | console.log(montaSanduba(pedido)) 112 | ``` 113 | 114 | > **Agora sim podemos entregar o sanduba!** 115 | 116 | ``` 117 | ➜ node examples/subway/subway.js 118 | 119 | Sanduba fechado com: 120 | Integral 15(quente) 121 | Atum(quente) 122 | Suíço(quente) 123 | Alface 124 | Cebolas 125 | Azeitonas pretas 126 | Picles 127 | Mostarda e Mel 128 | Cebola Agridoce 129 | Chipotle 130 | 131 | ``` 132 | 133 | ![Sanduíche de atum](https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/master/assets/images/sanduba.png) -------------------------------------------------------------------------------- /examples/subway/subway.js: -------------------------------------------------------------------------------- 1 | const paes = ['Integral', 2 | '9 grãos', 3 | '9 grãos com aveia e mel', 4 | 'Italiano', 5 | 'Parmesão e orégano', 6 | 'Três Queijos'] 7 | const tamanhos = [15, 30] 8 | const recheios = ['Vegetariano', 'Atum', 'Italiano', 'Frango'] 9 | const queijos = ['Cheddar', 'Prato', 'Suíço'] 10 | const saladas = ['Alface', 11 | 'Tomates', 12 | 'Pepinos', 13 | 'Pimentões verdes', 14 | 'Cebolas', 15 | 'Azeitonas pretas', 16 | 'Picles'] 17 | const molhos = ['Mostarda e Mel', 18 | 'Cebola Agridoce', 19 | 'Barbecue', 20 | 'Parmesão', 21 | 'Chipotle', 22 | 'Mostarda', 23 | 'Maionese'] 24 | 25 | const menu = { paes, tamanhos, recheios, queijos, saladas, molhos } 26 | 27 | const pedido = { 28 | pao: ['Integral'], 29 | tamanho: [15], 30 | recheio: ['Atum'], 31 | queijo: ['Suíço'], 32 | quente: true, 33 | saladas: ['Alface', 34 | 'Cebolas', 35 | 'Azeitonas pretas', 36 | 'Picles' 37 | ], 38 | molhos: ['Mostarda e Mel', 39 | 'Cebola Agridoce', 40 | 'Chipotle' 41 | ] 42 | } 43 | 44 | const montaSanduba = (sanduba) => { 45 | 46 | const escolhaPao = (pao) => sanduba.pao.includes(pao) 47 | const escolhaTamanho = (tamanho) => sanduba.tamanho.includes(tamanho) 48 | const escolhaRecheio = (recheio) => sanduba.recheio.includes(recheio) 49 | const escolhaQueijo = (queijo) => sanduba.queijo.includes(queijo) 50 | const escolhaSaladas = (salada) => sanduba.saladas.includes(salada) 51 | const escolhaMolhos = (molho) => sanduba.molhos.includes(molho) 52 | const fechaSanduba = (sanduba, ingrediente) => sanduba + '\r\n' + ingrediente 53 | const esquentar = (ingrediente, i) => (!Number.isInteger(ingrediente) 54 | && sanduba.quente) 55 | ? ingrediente + '(quente)' 56 | : ingrediente 57 | 58 | return '\r\nSanduba fechado com: ' + 59 | [ menu.paes.filter(escolhaPao) + ' ' 60 | + menu.tamanhos.filter(escolhaTamanho), 61 | ...menu.recheios.filter(escolhaRecheio), 62 | ...menu.queijos.filter(escolhaQueijo) 63 | ] 64 | .map(esquentar) 65 | .concat([ 66 | ...menu.saladas.filter(escolhaSaladas), 67 | ...menu.molhos.filter(escolhaMolhos) 68 | ]) 69 | .reduce(fechaSanduba, '') 70 | } 71 | 72 | console.log(montaSanduba(pedido)) -------------------------------------------------------------------------------- /examples/test/filter.funcional.spec.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect 2 | 3 | const filter = require('./../actions/filter.funcional') 4 | const value = 2 5 | const values = [1, 2, 3, 4, 5] 6 | const isEven = (value) => !(value % 2) 7 | 8 | describe('Filter', () => { 9 | describe('Número pares', () => { 10 | 11 | const resultadoRecebido = filter(isEven, values) 12 | const resultadoEsperado = [2, 4] 13 | 14 | it('deve retornar um ERRO caso não seja Array', () => { 15 | expect(() => filter(isEven, value)).to.throw(TypeError) 16 | }) 17 | 18 | it('deve retornar um Array', () => { 19 | expect(resultadoRecebido).to.be.an('array') 20 | }) 21 | 22 | it('deve retornar os valores pares', () => { 23 | expect(resultadoRecebido).to.eql(resultadoEsperado) 24 | }) 25 | 26 | it('não deve retornar os valores ímpares', () => { 27 | expect(resultadoRecebido).to.be.not.eql([1, 3, 5]) 28 | }) 29 | }) 30 | }) -------------------------------------------------------------------------------- /examples/test/filter.nosso.spec.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect 2 | 3 | const filter = require('./../actions/filter.nosso') 4 | const value = 2 5 | const values = [1, 2, 3, 4, 5] 6 | const isEven = (value) => !(value % 2) 7 | 8 | describe('Filter', () => { 9 | describe('Número pares', () => { 10 | 11 | const resultadoRecebido = filter(values, isEven) 12 | const resultadoEsperado = [2, 4] 13 | 14 | it('deve retornar um ERRO caso não seja Array', () => { 15 | expect(() => filter(value, isEven)).to.throw(TypeError) 16 | }) 17 | 18 | it('deve retornar um Array', () => { 19 | expect(resultadoRecebido).to.be.an('array') 20 | }) 21 | 22 | it('deve retornar os valores pares', () => { 23 | expect(resultadoRecebido).to.eql(resultadoEsperado) 24 | }) 25 | 26 | it('não deve retornar os valores ímpares', () => { 27 | expect(resultadoRecebido).to.be.not.eql([1, 3, 5]) 28 | }) 29 | }) 30 | }) -------------------------------------------------------------------------------- /examples/test/filter.spec.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect 2 | 3 | const filter = require('./../actions/filter') 4 | const value = 2 5 | const values = [1, 2, 3, 4, 5] 6 | const isEven = (value) => !(value % 2) 7 | 8 | describe('Filter', () => { 9 | describe('Número pares', () => { 10 | 11 | const resultadoRecebido = filter(values, isEven) 12 | const resultadoEsperado = [2, 4] 13 | 14 | it('deve retornar um ERRO caso não seja Array', () => { 15 | expect(() => filter(value, isEven)).to.throw(TypeError) 16 | }) 17 | 18 | it('deve retornar um Array', () => { 19 | expect(resultadoRecebido).to.be.an('array') 20 | }) 21 | 22 | it('deve retornar os valores pares', () => { 23 | expect(resultadoRecebido).to.eql(resultadoEsperado) 24 | }) 25 | 26 | it('não deve retornar os valores ímpares', () => { 27 | expect(resultadoRecebido).to.be.not.eql([1, 3, 5]) 28 | }) 29 | }) 30 | }) -------------------------------------------------------------------------------- /examples/test/map.funcional.spec.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect 2 | 3 | const map = require('./../actions/map.funcional') 4 | const value = 2 5 | const values = [1, 2, 3, 4, 5] 6 | const times10 = (value) => value * 10 7 | 8 | describe('Map', () => { 9 | describe('Array', () => { 10 | 11 | const resultadoRecebido = map(times10, values) 12 | const resultadoEsperado = [10, 20, 30, 40, 50] 13 | 14 | it('deve retornar um ERRO caso não seja Array', () => { 15 | expect(() => map(times10, value)).to.throw(TypeError) 16 | }) 17 | 18 | it('deve retornar um Array', () => { 19 | expect(resultadoRecebido).to.be.an('array') 20 | }) 21 | 22 | it('deve retornar os valor antigos multiplicados por 10', () => { 23 | expect(resultadoRecebido).to.eql(resultadoEsperado) 24 | }) 25 | }) 26 | }) -------------------------------------------------------------------------------- /examples/test/map.nosso.spec.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect 2 | 3 | const map = require('./../actions/map.nosso') 4 | const value = 2 5 | const values = [1, 2, 3, 4, 5] 6 | const times10 = (value) => value * 10 7 | 8 | describe('Map', () => { 9 | 10 | describe('Array', () => { 11 | 12 | const resultadoRecebido = map(values, times10) 13 | const resultadoEsperado = [10, 20, 30, 40, 50] 14 | 15 | it('deve retornar um ERRO caso não seja Array', () => { 16 | expect(() => map(2, times10)).to.throw(TypeError) 17 | }) 18 | 19 | it('deve retornar um Array', () => { 20 | expect(resultadoRecebido).to.be.an('array') 21 | }) 22 | 23 | it('deve retornar os valor antigos multiplicados por 10', () => { 24 | expect(resultadoRecebido).to.eql(resultadoEsperado) 25 | }) 26 | }) 27 | }) -------------------------------------------------------------------------------- /examples/test/map.spec.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect 2 | 3 | const map = require('./../actions/map') 4 | const value = 2 5 | const values = [1, 2, 3, 4, 5] 6 | const times10 = (value) => value * 10 7 | 8 | describe('Map', () => { 9 | 10 | // describe('Number', () => { 11 | 12 | // const resultadoRecebido = map(value, times10) 13 | // const resultadoEsperado = 20 14 | 15 | 16 | // it('deve retornar um Number', () => { 17 | // expect(resultadoRecebido).to.be.a('number') 18 | // }) 19 | 20 | // it(`deve retornar o valor antigo ${value} multiplicado por 10`, () => { 21 | // expect(resultadoRecebido).to.eql(resultadoEsperado) 22 | // }) 23 | // }) 24 | 25 | describe('Array', () => { 26 | 27 | const resultadoRecebido = map(values, times10) 28 | const resultadoEsperado = [10, 20, 30, 40, 50] 29 | 30 | it('deve retornar um ERRO caso não seja Array', () => { 31 | expect(() => map(2, times10)).to.throw(TypeError) 32 | }) 33 | 34 | it('deve retornar um Array', () => { 35 | expect(resultadoRecebido).to.be.an('array') 36 | }) 37 | 38 | it('deve retornar os valor antigos multiplicados por 10', () => { 39 | expect(resultadoRecebido).to.eql(resultadoEsperado) 40 | }) 41 | }) 42 | }) -------------------------------------------------------------------------------- /images/bicho-de-sete-cabecas.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/images/bicho-de-sete-cabecas.jpg -------------------------------------------------------------------------------- /images/function inside function.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/images/function inside function.jpg -------------------------------------------------------------------------------- /images/hadoop-map-reduce.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/images/hadoop-map-reduce.png -------------------------------------------------------------------------------- /images/homer-doh.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/images/homer-doh.gif -------------------------------------------------------------------------------- /images/logo-js-funcional.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/images/logo-js-funcional.gif -------------------------------------------------------------------------------- /images/logo-webschool-js-funcional.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/images/logo-webschool-js-funcional.png -------------------------------------------------------------------------------- /images/logo-webschool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/images/logo-webschool.png -------------------------------------------------------------------------------- /images/meme-hein.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/images/meme-hein.gif -------------------------------------------------------------------------------- /images/meme-jackie-chan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/images/meme-jackie-chan.jpg -------------------------------------------------------------------------------- /images/meme-matematica.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/images/meme-matematica.png -------------------------------------------------------------------------------- /images/meme-pode-isso-arnaldo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/images/meme-pode-isso-arnaldo.jpg -------------------------------------------------------------------------------- /images/meme-realy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/images/meme-realy.jpg -------------------------------------------------------------------------------- /images/meme-shit-happens.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/images/meme-shit-happens.jpg -------------------------------------------------------------------------------- /images/meme-talk-is-cheap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/images/meme-talk-is-cheap.jpg -------------------------------------------------------------------------------- /images/meme-wat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/images/meme-wat.jpg -------------------------------------------------------------------------------- /images/meme-yeah-we-will-sse-about-that.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/images/meme-yeah-we-will-sse-about-that.jpeg -------------------------------------------------------------------------------- /images/meme_pensando_png_by_mfsyrcm-d5949t0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/images/meme_pensando_png_by_mfsyrcm-d5949t0.png -------------------------------------------------------------------------------- /images/monad_tutorial.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/images/monad_tutorial.jpg -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workshop-js-funcional-free", 3 | "version": "1.0.0", 4 | "description": "Funcional for All", 5 | "main": "examples.js", 6 | "directories": { 7 | "example": "examples" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/Webschool-io/workshop-js-funcional-free.git" 15 | }, 16 | "keywords": [ 17 | "funcional", 18 | "suissa", 19 | "webschool", 20 | "programação", 21 | "funcional", 22 | "functional", 23 | "javascript", 24 | "map", 25 | "reduce", 26 | "filter" 27 | ], 28 | "author": "Suissa", 29 | "license": "WTFPL", 30 | "bugs": { 31 | "url": "https://github.com/Webschool-io/workshop-js-funcional-free/issues" 32 | }, 33 | "homepage": "https://github.com/Webschool-io/workshop-js-funcional-free#readme", 34 | "dependencies": { 35 | "chai": "^3.5.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sergin-malan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/sergin-malan.png -------------------------------------------------------------------------------- /slides/images/slide-cover-aula-5-parte-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/slides/images/slide-cover-aula-5-parte-2.png -------------------------------------------------------------------------------- /slides/images/slide-cover-aula-5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/slides/images/slide-cover-aula-5.gif -------------------------------------------------------------------------------- /slides/images/slide-cover-aula-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/slides/images/slide-cover-aula-5.png -------------------------------------------------------------------------------- /slides/images/slide-cover-aula.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Webschool-io/workshop-js-funcional-free/42e8a09387214bb5d5b2e8f6b84360e99f83d365/slides/images/slide-cover-aula.psd --------------------------------------------------------------------------------