• Stars
    star
    356
  • Rank 119,446 (Top 3 %)
  • Language
  • License
    MIT License
  • Created about 7 years ago
  • Updated over 6 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Tips and guidelines for real world functional code-bases in JS

Real world functional programming in JS

Tips and guidelines for scalable and easily maintainable code bases!

Summary

Functional programming in JavaScript in 2017 is finally a DO. Most of the new tools for this environment are based on functional concepts and it seems like functional programming is becoming more and more popular due to its advantages. On Rung, functional programming is the main mindset for all projects and subprojects and it scales really well. We have been using functional programming deeply in the last 2 years, from JS up to Haskell. Bugs got less frequent and easier to track and fix, and, different from what you may think, our team got it really fast! Keeping the code standard and working with several people without worrying with ones breaking each other code is now a relief! The tips below are not empiric, they are being used daily by us to deliver high quality code.

Recommended libraries

Ramda

Ramda is like Lodash, but made right. It has a lot of functions to work with data and to work with composition. Unlike Lodash, in Ramda, functions come before data. This allows point-free programming (like Haskell).

Ramda Fantasy

Ramda Fantasy is a set of common monadic structures to work with values, errors, side-effects and state.

Folktale

Folktale is an alternative to Ramda Fantasy. It is better maintained and exports tools for working with concurrency, computations that may fail and modelling data using algebraic data types.

Do

Return everything

Everything should return, also functions that emit side-effects. Try to preserve homomorphism. Try to keep the return type of a function consistent. Don't mix computations that transform data with things like writing to screen. Modular code is easier to maintain. Use parameters for replaceable data instead of hard-coding.

Don't

let result = 1
for (let i = 2; i <= 5; i++) {
    result *= i
}

console.log('Fact of 5: ', result)

Do

const fact = n => n === 0
    ? 1
    : n * fact(n - 1)

console.log('Fact of 5: ', fact(5))

Tacit programming

Tacit programming is also known as point-free programming. It means, basically, using simple functions to compose more complexes functions (and omitting arguments). One of the functional programming pillars is composition.

Don't

console.log(
    sum(map(x => x + 1, range(0, 100)))
)

Do

const transform = pipe(map(inc), sum))

console.log(transform(range(0, 100)))

Use modules

Isolate your logic inside modules that do one thing, and do that well. Modules should export functions and, when using a type checker, types.

Composition over inheritance

Inheritance is definitely not how you deal with data and behavior in functional programming. Computations are modeled using behaviors. Some languages call them type classes.

Memoize functions

Memoize pure functions that are used several times with the same input parameters. Ramda provides a function called memoize for that!

Do

const fact = memoize(n => 0 === n
    ? 1
    : n * fact(n - 1))

fact(5) // Calculates fact for 5, 4, 3 ...
fact(5) // Instantaneous
fact(3) // Instantaneous

Avoid

Mutability

Mutability is evil! It can set your house on fire, kill your cat and buy costumes on e-bay using your credit card! Be careful!

Functional programming heavily relies on immutability. Redefining a value or the property of an object is therefore forbidden. Don't use neither var nor let. Everything should be a const. Don't use functions like .push or .splice because they change the value of the original parameter and are error-prone.

Don't

var approved = []

for (var i = 0; i < approved.length; i++) {
    if (users[i].score >= 7) {
        approved.push(approved)
    }
}

Do

const approved = filter(user => user.score >= 7, users)

Bare code

Code outside a function is a side-effect inside a module. Effects should be explicit. Only static declarations are allowed.

Don't

  • user.js
const usersList = User.find().asArray()

usersList.forEach(user => {
    console.log(user.name)
})

Do

  • user.js
const listUsers = () => User.find().asArray()

export const printNames = () => listUsers().forEach(user => {
    console.log(user.name)
})
  • index.js
import { printNames } from './user'

printNames()

Loops

Native statement loops are forbidden. Loops are made to enforce side-effects and there is no case of a loop where a side-effect wouldn't be expected. You don't need to use recursion most of the time. There is a lot of functions and compositions that you can use to achieve your logic. Ramda provides some functions like map, filter and reduce that make loops completely unnecessary.

Don't

const even = []
for (let i = 0; i <= 300; i++) {
    if (i % 2 === 0) {
        even.push(i)
    }
}

console.log(even) // [0, 2, 4, 6, 8 ...]

Do

import { filter, range } from 'ramda'

const even = filter(n => n % 2 === 0)

console.log(even(range(0, 300))) // [0, 2, 4, 6, 8 ...]

Switch

In functional programming, imperative structures do not exist. switch is intended to have effects and known to have a complex flux. You can use the function cond from Ramda instead. cond receives a list of pairs of functions where the first one is the predicate and the second is the transformation.

Don't

const person = { name: 'Wesley' }
let result

switch (person.name) {
    case 'Dayana':
        result = person.name + ' is cool!'
        break
    case 'Wesley':
        result = person.name + ' likes farting'
        break
    default:
        result = 'Who is ' + person.name + '?'
}

console.log(result) // Wesley likes farting

Do

import { T, cond, propEq } from 'ramda'

const getDescription = cond([
    [propEq('name', 'Dayana'), ({ name }) => `${name} is cool!`],
    [propEq('name', 'Wesley'), ({ name }) => `${name} likes farting`],
    [T, ({ name }) => `Who is ${name}?`]
])

console.log(getDescription({ name: 'Wesley' })) // Wesley likes farting

Try

Error handling shouldn't be handled by exceptions, but by either monads or promises.

Don't

try {
    undefined.property
} catch (err) {
    console.log(err)
}

Do

import { tryCatch } from 'ramda'
import { Either } from 'ramda-fantasy'

const computation = tryCatch(
    () => undefined.property,
    Either.Right,
    Either.Left
)

console.log(computation()) // Left<TypeError>

Undefined and null

Here lies the root of undefined is not a function. Missing values lead to over-engineering, lots of verifications and conditions and errors that break your application and cannot be caught in compile-time. You should replace them by monads, like the Maybe monad.

Don't

const safeDiv = (a, b) => {
    if (b === 0) {
        return undefined
    }

    return a / b
}

console.log(safeDiv(20, 0) + 10) // Ops

Do

import { Maybe } from 'ramda-fantasy'

const safeDiv = (a, b) => 0 === b
    ? Maybe.Nothing
    : Maybe.Just(a / b)

safeDiv(20, 0).chain(result => {
    console.log(result + 10) // Never falls here
})

Classes

In general, using classes enforce effects and directly mutability. You can replace them by literal objects and functions that work on these objects.

Don't

class Person {
    setName(name) { this.name = name }
    getName() { return this.name }
}

let person = new Person()
person.setName('Cassandra')

Do

import { lensProp, prop, set } from 'ramda'

const setName = set(lensProp('name'))
const getName = prop('name')

const person = setName('Cassandra', {})

Callbacks

Callbacks can guide you easily to Hadouken code. Promises or futures are the way to go here. If you are using a library that uses callbacks, you can promisify the function to transform the callback to a promise.

Don't

$.ajax('http://api.trello.com/me', me => {
    $.ajax(`http://api.trello.com/tasks/${me.id}`, tasks => {
        var finished = []
        for (var i = 0; i < tasks.length; i++) {
            if (tasks[i].done) {
                finished.push(tasks[i])
            }
        }
    })
})

Do

fetch('http://api.trello.com/me')
    .then(({ id }) => fetch(`http://api.trello.com/tasks/${id}`))
    .filter(prop('done'))
    .tap(console.log)

Prototype extension

Doing something like String.prototype.x = is like a virus! It spreads all over your code and infects every piece, possibly causing unexpected behavior. Prefer isolating these behaviors in functions.

Advantages

Optimization

Pure functions are easier to optimize. They can cached with their parameters, as long as having the same input will always generate the same output.

Testing

TDD is the way to go here. Pure functions are very easier to test and have no dependencies in general. Having a 100% coverage on a functional code base is a lot easier than in an imperative one.

Scalability

Functional code is easier to scale. Having, for example, a stateless server will allow you to have multiple instances of it in different machines without worrying with shared and dependent data. Running on clusters and multiple processors is possible and V8 can optimize to run pure computations on different processes.

Learning resources

Motivational articles

For beginners

Babel plugins

More Repositories

1

sclack

The best CLI client for Slack, because everything is terrible!
Python
2,450
star
2

gemidao-do-zap

Uma aplicação de linha de comando que liga para alguém tocando o gemidão do Zap
JavaScript
1,990
star
3

skype-unofficial-client

A Skype client for Linux built on top of node webkit
JavaScript
378
star
4

pragmatic-functional-javascript

The "Pragmatic Functional JavaScript" book
CSS
360
star
5

because-we-can

Not because we must do it, but because we can
PHP
116
star
6

babel-plugin-function-composition

Babel plugin to compose functions with piping using the & operator
JavaScript
63
star
7

papers-to-read

My curated list of papers I need to read (or already did), mostly about PLT
52
star
8

xgh-headers

Headers for "eXtreme Go Horse" projects
44
star
9

rawr

A word that means "I Love You" in dinosaur.
PHP
42
star
10

php-code-without-any-workaround

A proof of concept of a PHP code with 0 bugs and workarounds!
PHP
40
star
11

php-partial-function-application

First concise implementation of partial function application in PHP
PHP
31
star
12

php-maybe-monad

Maybe monad in PHP
PHP
30
star
13

babel-plugin-implicit-function

Babel plugin to allow lazy expressions by implicit arrow functions via ~ operator
JavaScript
24
star
14

php-pipe-operator-rfc

Proposal for implementation of the pipeline operator on PHP 7
23
star
15

gemidao-do-zap-free

A versão do Gemidão do Zap por ligação, mas sem token e gratuita
22
star
16

sudowoodo

Sudowoodo in your console!
Haskell
17
star
17

hindley-milner-js

Algorithm W implementation for type inference and parametric polymorphism in Vanilla JS
JavaScript
17
star
18

novas-regras-da-apda

Novas regras da APDA: make APDA great again
14
star
19

talks

Content about my talks in events
13
star
20

vimrc

My personal Vim configuration
Vim Script
10
star
21

rawrlang

A language that compiles to PHP.
PHP
9
star
22

php-church-encoding

Church encoded numerical computations over lambda-calculus implemented in PHP
PHP
9
star
23

phpp

(PHP Hypertext Preprocessor) Preprocessor
PHP
9
star
24

js-to-advpl

Compile JavaScript to AdvPL and run inside Protheus
JavaScript
8
star
25

advpl-coding-standards

Coding standards for Advanced Protheus Language
6
star
26

vardump.js

A Javascript implementation of PHP's var_dump for expression analysis
JavaScript
6
star
27

fwquerybuilder

AdvPL/Harbour SQL query builder
xBase
6
star
28

iguana-wallpapers

Wallpapers for Iguana OS.
PHP
3
star
29

jamda

🍯 Practical functional Java
3
star
30

honk

The honk button everyone needs
HTML
3
star
31

drag-queen

The npm package to work with drag queens
JavaScript
3
star
32

extended-zpl-deprecated

A language that compiles to ZPL
PHP
3
star
33

rent-a-capy

Cheap. Cute. Lovely.
HTML
3
star
34

capy-tracker

A Node-compliant user tracking library made with love and JS
JavaScript
3
star
35

clube-dos-programadores

Regras, definições e links úteis
3
star
36

hy-weed-card-game

Weed card game written in Hy.
Hy
3
star
37

lgtv-youtube-cast

Adds a "cast" button to Youtube that allows you to use your notebook to play videos on LGTV, like your phone
2
star
38

prelude-erl

A port of Prelude-LS to Erlang.
Erlang
2
star
39

seq

Awesome list handling using first class functions in Java. A port of prelude-ls.
Java
2
star
40

awesome-ppx-ocaml

Curated list of awesome OCaml ppx tools and rewriters
2
star
41

sequela

The magic SQL parser for JS!
JavaScript
2
star
42

php-additional-splat-usage-rfc

RFC draft for spread operator for array literals
2
star
43

awesome-gemidao

A curated list of all repositories with focus on Gemidão do Zap
2
star
44

javascript-the-good-parts

2
star
45

htpl

HyperText Programming Language - Yes, it is possible!
PHP
2
star
46

vim-advpl

Vim mode for Advanced Protheus Language
Vim Script
2
star
47

webpack-dsl

A meta language to describe webpack configuration
2
star
48

pop-divas-good-morning-generator

Command line tool for generating "good morning" images with pop divas pictures
2
star
49

node-server

Tiny REST server demo!
JavaScript
1
star
50

sweet-currying

Implicit currying with λ functions
JavaScript
1
star
51

livescript-webpack-boilerplate

LiveScript and Webpack boilerplate for LiveScript library projects
LiveScript
1
star
52

fish-git-loc

Fish plugin for giving lines of code of a git repository
Shell
1
star
53

frida

Frida is a LISP dialect that runs on top of Node.js
LiveScript
1
star
54

paa

Projeto e Análise de Algoritmos - UDESC
OCaml
1
star
55

monadic-prelude

Monadic secure functions to deal with data in Haskell
Haskell
1
star
56

yay-partial-functions

Partial functions for Yay preprocessor
PHP
1
star
57

haskellcamargo.github.io

My personal blog
HTML
1
star