├── LICENSE └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Marcelo Camargo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Real world functional programming in JS 2 | 3 | Tips and guidelines for scalable and easily maintainable code bases! 4 | 5 | ![](https://raw.github.com/fantasyland/fantasy-land/master/logo.png) 6 | 7 | ## Summary 8 | 9 | - [Recommended libraries](#recommended-libraries) 10 | - [Ramda](#ramda) 11 | - [Ramda Fantasy](#ramda-fantasy) 12 | - [Do](#do) 13 | - [Return everything](#return-everything) 14 | - [Tacit programming](#tacit-programming) 15 | - [Use modules](#use-modules) 16 | - [Composition over inheritance](#composition-over-inheritance) 17 | - [Memoize functions](#memoize-functions) 18 | - [Avoid](#avoid) 19 | - [Mutability](#mutability) 20 | - [Loops](#loops) 21 | - [Switch](#switch) 22 | - [Try](#try) 23 | - [Undefined and null](#undefined-and-null) 24 | - [Classes](#classes) 25 | - [Callbacks](#callbacks) 26 | - [Prototype extension](#prototype-extension) 27 | - [Advantages](#advantages) 28 | - [Optimization](#optimization) 29 | - [Testing](#testing) 30 | - [Scalability](#scalability) 31 | - [Learning resources](#learning-resources) 32 | - [Motivational articles](#motivational-articles) 33 | - [For beginners](#for-beginners) 34 | - [Babel plugins](#babel-plugins) 35 | 36 | Functional programming in JavaScript in 2017 is finally a **DO**. Most of the 37 | new tools for this environment are based on functional concepts and it seems 38 | like functional programming is becoming more and more popular due to its 39 | advantages. On [Rung](https://developers.rung.com.br/), functional programming 40 | is the main mindset for all projects and subprojects and it scales really well. 41 | We have been using functional programming deeply in the last 2 years, from 42 | JS up to Haskell. Bugs got less frequent and easier to track and fix, and, 43 | different from what you may think, our team got it **really fast**! Keeping 44 | the code standard and working with several people without worrying with 45 | ones breaking each other code is now a relief! The tips below are not empiric, 46 | they are being used daily by us to deliver high quality code. 47 | 48 | ## Recommended libraries 49 | 50 | ### Ramda 51 | 52 | [Ramda](https://github.com/ramda/ramda) is like Lodash, but made right. It has a lot 53 | of functions to work with data and to work with composition. Unlike Lodash, in 54 | Ramda, functions come before data. This allows point-free programming (like Haskell). 55 | 56 | ### Ramda Fantasy 57 | 58 | [Ramda Fantasy](https://github.com/ramda/ramda-fantasy) is a set of common monadic 59 | structures to work with values, errors, side-effects and state. 60 | 61 | ## Folktale 62 | 63 | [Folktale](http://folktale.origamitower.com/) is an alternative to Ramda Fantasy. It 64 | is better maintained and exports tools for working with concurrency, computations that 65 | may fail and modelling data using algebraic data types. 66 | 67 | ## Do 68 | 69 | ### Return everything 70 | 71 | Everything should return, also functions that emit side-effects. Try to preserve 72 | homomorphism. Try to keep the return type of a function consistent. Don't mix 73 | computations that transform data with things like writing to screen. Modular code 74 | is easier to maintain. Use parameters for replaceable data instead of hard-coding. 75 | 76 | #### Don't 77 | 78 | ```js 79 | let result = 1 80 | for (let i = 2; i <= 5; i++) { 81 | result *= i 82 | } 83 | 84 | console.log('Fact of 5: ', result) 85 | ``` 86 | 87 | #### Do 88 | 89 | ```js 90 | const fact = n => n === 0 91 | ? 1 92 | : n * fact(n - 1) 93 | 94 | console.log('Fact of 5: ', fact(5)) 95 | ``` 96 | 97 | 98 | ### Tacit programming 99 | 100 | Tacit programming is also known as point-free programming. It means, basically, using 101 | simple functions to compose more complexes functions (and omitting arguments). One of 102 | the functional programming pillars is composition. 103 | 104 | ### Don't 105 | 106 | ```js 107 | console.log( 108 | sum(map(x => x + 1, range(0, 100))) 109 | ) 110 | ``` 111 | 112 | ### Do 113 | 114 | ```js 115 | const transform = pipe(map(inc), sum)) 116 | 117 | console.log(transform(range(0, 100))) 118 | ``` 119 | 120 | ### Use modules 121 | 122 | Isolate your logic inside modules that do one thing, and do that well. Modules 123 | should export functions and, when using a type checker, types. 124 | 125 | ### Composition over inheritance 126 | 127 | Inheritance is definitely **not** how you deal with data and behavior in functional programming. 128 | Computations are modeled using behaviors. Some languages call them type classes. 129 | 130 | ### Memoize functions 131 | 132 | Memoize pure functions that are used several times with the same input parameters. Ramda provides 133 | a function called `memoize` for that! 134 | 135 | #### Do 136 | 137 | ```js 138 | const fact = memoize(n => 0 === n 139 | ? 1 140 | : n * fact(n - 1)) 141 | 142 | fact(5) // Calculates fact for 5, 4, 3 ... 143 | fact(5) // Instantaneous 144 | fact(3) // Instantaneous 145 | ``` 146 | 147 | ## Avoid 148 | 149 | ### Mutability 150 | 151 | > Mutability is evil! It can set your house on fire, kill your cat and buy costumes on e-bay 152 | > using your credit card! Be careful! 153 | 154 | Functional programming heavily relies on immutability. Redefining a value or the property of an 155 | object is therefore forbidden. Don't use neither `var` nor `let`. Everything should be a `const`. 156 | Don't use functions like `.push` or `.splice` because they change the value of the original 157 | parameter and are error-prone. 158 | 159 | #### Don't 160 | 161 | ```js 162 | var approved = [] 163 | 164 | for (var i = 0; i < approved.length; i++) { 165 | if (users[i].score >= 7) { 166 | approved.push(approved) 167 | } 168 | } 169 | ``` 170 | 171 | #### Do 172 | 173 | ```js 174 | const approved = filter(user => user.score >= 7, users) 175 | ``` 176 | 177 | ### Bare code 178 | 179 | Code outside a function is a side-effect inside a module. Effects should be explicit. Only 180 | static declarations are allowed. 181 | 182 | #### Don't 183 | 184 | - `user.js` 185 | ```js 186 | const usersList = User.find().asArray() 187 | 188 | usersList.forEach(user => { 189 | console.log(user.name) 190 | }) 191 | ``` 192 | 193 | #### Do 194 | 195 | - `user.js` 196 | ```js 197 | const listUsers = () => User.find().asArray() 198 | 199 | export const printNames = () => listUsers().forEach(user => { 200 | console.log(user.name) 201 | }) 202 | ``` 203 | 204 | - `index.js` 205 | ```js 206 | import { printNames } from './user' 207 | 208 | printNames() 209 | ``` 210 | 211 | ### Loops 212 | 213 | Native statement loops are forbidden. Loops are made to enforce side-effects and there is no 214 | case of a loop where a side-effect wouldn't be expected. You don't need to use recursion most of 215 | the time. There is a lot of functions and compositions that you can use to achieve your logic. 216 | Ramda provides some functions like `map`, `filter` and `reduce` that make loops completely 217 | unnecessary. 218 | 219 | #### Don't 220 | 221 | ```js 222 | const even = [] 223 | for (let i = 0; i <= 300; i++) { 224 | if (i % 2 === 0) { 225 | even.push(i) 226 | } 227 | } 228 | 229 | console.log(even) // [0, 2, 4, 6, 8 ...] 230 | ``` 231 | 232 | #### Do 233 | 234 | ```js 235 | import { filter, range } from 'ramda' 236 | 237 | const even = filter(n => n % 2 === 0) 238 | 239 | console.log(even(range(0, 300))) // [0, 2, 4, 6, 8 ...] 240 | ``` 241 | 242 | ### Switch 243 | 244 | In functional programming, imperative structures do not exist. `switch` is intended to have 245 | effects and known to have a complex flux. You can use the function `cond` from Ramda instead. 246 | `cond` receives a list of pairs of functions where the first one is the predicate and the 247 | second is the transformation. 248 | 249 | #### Don't 250 | 251 | ```js 252 | const person = { name: 'Wesley' } 253 | let result 254 | 255 | switch (person.name) { 256 | case 'Dayana': 257 | result = person.name + ' is cool!' 258 | break 259 | case 'Wesley': 260 | result = person.name + ' likes farting' 261 | break 262 | default: 263 | result = 'Who is ' + person.name + '?' 264 | } 265 | 266 | console.log(result) // Wesley likes farting 267 | ``` 268 | 269 | ### Do 270 | 271 | ```js 272 | import { T, cond, propEq } from 'ramda' 273 | 274 | const getDescription = cond([ 275 | [propEq('name', 'Dayana'), ({ name }) => `${name} is cool!`], 276 | [propEq('name', 'Wesley'), ({ name }) => `${name} likes farting`], 277 | [T, ({ name }) => `Who is ${name}?`] 278 | ]) 279 | 280 | console.log(getDescription({ name: 'Wesley' })) // Wesley likes farting 281 | ``` 282 | 283 | ### Try 284 | 285 | Error handling shouldn't be handled by exceptions, but by either monads or promises. 286 | 287 | #### Don't 288 | 289 | ```js 290 | try { 291 | undefined.property 292 | } catch (err) { 293 | console.log(err) 294 | } 295 | ``` 296 | 297 | #### Do 298 | 299 | ```js 300 | import { tryCatch } from 'ramda' 301 | import { Either } from 'ramda-fantasy' 302 | 303 | const computation = tryCatch( 304 | () => undefined.property, 305 | Either.Right, 306 | Either.Left 307 | ) 308 | 309 | console.log(computation()) // Left 310 | ``` 311 | 312 | ### Undefined and null 313 | 314 | Here lies the root of _undefined is not a function_. Missing values lead to over-engineering, 315 | lots of verifications and conditions and errors that break your application and cannot 316 | be caught in compile-time. You should replace them by monads, like the `Maybe` monad. 317 | 318 | #### Don't 319 | 320 | ```js 321 | const safeDiv = (a, b) => { 322 | if (b === 0) { 323 | return undefined 324 | } 325 | 326 | return a / b 327 | } 328 | 329 | console.log(safeDiv(20, 0) + 10) // Ops 330 | ``` 331 | 332 | #### Do 333 | 334 | ```js 335 | import { Maybe } from 'ramda-fantasy' 336 | 337 | const safeDiv = (a, b) => 0 === b 338 | ? Maybe.Nothing 339 | : Maybe.Just(a / b) 340 | 341 | safeDiv(20, 0).chain(result => { 342 | console.log(result + 10) // Never falls here 343 | }) 344 | ``` 345 | 346 | ### Classes 347 | 348 | In general, using classes enforce effects and directly mutability. You can replace them by literal 349 | objects and functions that work on these objects. 350 | 351 | #### Don't 352 | 353 | ```js 354 | class Person { 355 | setName(name) { this.name = name } 356 | getName() { return this.name } 357 | } 358 | 359 | let person = new Person() 360 | person.setName('Cassandra') 361 | ``` 362 | 363 | #### Do 364 | 365 | ```js 366 | import { lensProp, prop, set } from 'ramda' 367 | 368 | const setName = set(lensProp('name')) 369 | const getName = prop('name') 370 | 371 | const person = setName('Cassandra', {}) 372 | ``` 373 | 374 | ### Callbacks 375 | 376 | Callbacks can guide you easily to _Hadouken_ code. Promises or _futures_ are the way to go here. 377 | If you are using a library that uses callbacks, you can _promisify_ the function to transform the 378 | callback to a promise. 379 | 380 | #### Don't 381 | 382 | ```js 383 | $.ajax('http://api.trello.com/me', me => { 384 | $.ajax(`http://api.trello.com/tasks/${me.id}`, tasks => { 385 | var finished = [] 386 | for (var i = 0; i < tasks.length; i++) { 387 | if (tasks[i].done) { 388 | finished.push(tasks[i]) 389 | } 390 | } 391 | }) 392 | }) 393 | ``` 394 | 395 | #### Do 396 | 397 | ```js 398 | fetch('http://api.trello.com/me') 399 | .then(({ id }) => fetch(`http://api.trello.com/tasks/${id}`)) 400 | .filter(prop('done')) 401 | .tap(console.log) 402 | ``` 403 | 404 | ### Prototype extension 405 | 406 | Doing something like `String.prototype.x =` is like a virus! It spreads all over your code and 407 | infects every piece, possibly causing unexpected behavior. Prefer isolating these behaviors in 408 | functions. 409 | 410 | ## Advantages 411 | 412 | ### Optimization 413 | 414 | Pure functions are easier to optimize. They can cached with their parameters, as long as having 415 | the same input will always generate the same output. 416 | 417 | ### Testing 418 | 419 | TDD is the way to go here. Pure functions are very easier to test and have no dependencies in 420 | general. Having a 100% coverage on a functional code base is a lot easier than in an imperative 421 | one. 422 | 423 | ### Scalability 424 | 425 | Functional code is easier to scale. Having, for example, a stateless server will allow you to 426 | have multiple instances of it in different machines without worrying with shared and dependent 427 | data. Running on clusters and multiple processors is possible and V8 can optimize to run 428 | pure computations on different processes. 429 | 430 | # Learning resources 431 | 432 | ## Motivational articles 433 | 434 | - [Functional Programming should be your #1 priority](https://medium.com/@jugoncalves/functional-programming-should-be-your-1-priority-for-2015-47dd4641d6b9) 435 | 436 | ## For beginners 437 | 438 | - [Functors, Applicatives, And Monads In Pictures](http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html) 439 | 440 | ## Babel plugins 441 | 442 | - [Function composition](https://github.com/haskellcamargo/babel-plugin-function-composition) 443 | - [Implicit function](https://github.com/haskellcamargo/babel-plugin-implicit-function) 444 | - [Pipe operator](https://github.com/Swizz/babel-plugin-pipe-operator-curry) 445 | --------------------------------------------------------------------------------