Skip to content

Instantly share code, notes, and snippets.

@brekk
Last active August 28, 2020 12:07
Show Gist options
  • Save brekk/2b5a8ed82f535a5d7ddc87cec2270a1a to your computer and use it in GitHub Desktop.
Save brekk/2b5a8ed82f535a5d7ddc87cec2270a1a to your computer and use it in GitHub Desktop.
FP Code Convention Template

Code Conventions

This codebase uses a functional programming (FP) approach and ideology. For anything which cannot be automatically linted for and resolved automatically, this document will be used to frame conventions used within the code and their rationale.

Use ramda

Use ramda for function composition, data transformation, higher-order functions and automatically curried interface. It is well tested, offers more functions than you will likely ever need to use, but has a core set of very clean utililty functions.

See converter for a good example of potential usage.

We also have eslint-plugin-ramda enabled, and it will guide you in some cases which can be statically found.

Curry Everything

Unless explicitly specified, all exported functions should be curried if they are greater than arity 1. This aids equational reasoning, allows for partial application and allows for higher-order function transformation.

Incorrect Code

const wrapStringWithString = (outer, inner) => {
  return outer[0] + inner + outer[1]
}

// INCORRECT: pipe(wrapStringWithString(['^', '$']))('cool')

Better Code

const wrapStringWithString = curry(function _wrapStringWithString(outer, inner) {
  return outer[0] + inner + outer[1]
})

// CORRECT: pipe(wrapStringWithString(['^', '$']))('cool')
// INCORRECT: pipe(wrapStringWithString([]))('cool')

Even Better Code

const wrapStringWithString = curry(function _wrapStringWithString(outer, inner) {
  return pathOr('', 0, outer) + inner + pathOr('', 1, outer)
})


// CORRECT: pipe(wrapStringWithString(['^', '$']))('cool')
// CORRECT: pipe(wrapStringWithString([]))('cool')

Use Named Function Expressions

At the highest level (beneath curry, likely), one should use named function expressions, because transpiled code often treats arrow functions and anonymous functions differently than named functions. This is minor but often aids debugging immensely.

Incorrect Code

const myFunction = curry((a, b) => a + b))

Better Code

const myFunction = curry(function _myFunction(a, b) { return a + b })

Use fluture

This codebase uses fluture to provide a sane asynchronous interface which is lazy and monadic in nature. There are a number of attractive features which are a marked improvement from Promises / async + await. If you are conceptually familiar with Promises, this is a good resource for understanding the trade-offs. tl;dr

new Promise ((res) => { setTimeout (res, 200, 'Hello') })
.then (x => `${x} world!`)
.then (x => Promise.fromNode (done => fs.writeFile ('hello.txt', x, done)))
.then (console.log, console.error);
//vs
pipe(
  () => Future((rej, res) => { setTimeout (res, 200, 'Hello') }),
  map (x => `${x} world!`),
  chain (x => Future.node (done => fs.writeFile ('hello.txt', x, done))),
  fork(console.error)(console.log)
)()

See cli for a good example of potential usage.

Fluture Troubleshooting

  • Use map to access / transform the contained value of the Future
  • Use chain to access / combine a different Future when you already have one
  • Use fork to invoke the actual asynchronous request. IF YOU DO NOT FORK, YOU WILL NEVER DO ANYTHING.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment