Skip to content

Latest commit

 

History

History
237 lines (160 loc) · 5.82 KB

PITCHME.md

File metadata and controls

237 lines (160 loc) · 5.82 KB

10 Rules of the 'Functional River' Pattern

Or, "Mastering modern Functional JavaScript"

By: Dan Levy Follow me on twitter/github: @justsml


  1. Code like a story
  2. Eliminate ad hoc code
  3. Similar async & sync patterns (when possible)
  4. Uniform interfaces
  5. Well defined & changeable pathways
  6. Flat & highly modular
  7. Limit complexity score
  8. Immutable if possible
  9. Favor pure & stateless functions

1: Code a story

For details, press DOWN arrow/key

For next rule, press RIGHT arrow/key +++ Code should be readable for the next human. It helps to keep function complexity low (Rule No. 7).

In my opinion, a pipeline-style pattern should be used by all Public methods in a module.

image


2: Remove ad hoc logic

For details, press DOWN arrow/key

For next rule, press RIGHT arrow/key

+++

What is ad hoc logic?

It is a common violation of Single Responsibility (from SOLID). Identifying it only takes a little practice and discipline.

+++

+++

Martin Fowler on Function Length: image +++ An increase in Function Length correlates with rising ad hoc logic. +++ A perfectly composed HoC/function is devoid of branching (if's).

If a function must be long HoC (a Higher Order Component), the lowest cost possible will always be a function chain

+++

To start, move your validation or data checks to their own functions. Give your instructions meaning.

These 4 'red flags' are very helpful in your search:

+++

  • 10+ expressions/statements in a function
  • Co-mingling if's with function calls inside huge 'super functions'
  • Any let/const/var mid-function definition
  • More than 1-3 lines before a return

Bottom line: Avoid complex execution trees. Tiny functions usually always win.


3: Uniform sync and async

For details, press DOWN arrow/key

For next rule, press RIGHT arrow/key

+++

This is not literally desireable everywhere. However there is something really useful about utility functions & Bluebird Promise's array methods...

+++

Can you make isTest function (next slide) async without affecting other code? +++

const leads = [...]
// can you make isTest async w/ no impl. changes?
const isTest = (x) => x.test === true
return leads.map(isTest)

+++

Bluebird Promises solve this by extending Array.prototype methods into the "Promises" space.

+++

// Get a promise wrapped array
const leads = Promise.resolve([...])
// Here's an async version of isTest():
const isTestAsync = (x) => Promise.resolve(x.test)

return leads.map(isTestAsync)
Using the sync or async isTest/isTestAsync methods is same when using a Functional River!

4: Uniform functions

For details, press DOWN arrow/key

For next rule, press RIGHT arrow/key +++ image

+++

There are a few cases where 2+ parameter functions make sense. Here is a great example from NodeJS's FS API:

fs.renameSync(oldPath, newPath)

+++ In the context of dynamic languages, always prefer single argument funtions.

3 key enablers of uniform Function APIs:
Inline hash/object definition + Object Destructuring + Parameter Defaults

+++

A compelling path to reusable code. With a strong functional emphasis. the start of reusable code.


5: Simple Paths

For details, press DOWN arrow/key

For next rule, press RIGHT arrow/key

+++

Here's how too many 'exit points' leads to a delicate & unreadable mess... [press down arrow] +++

image

+++

For every if, for, while, switch, throw or return you add to your function, you layer on the pathways.

Avoid code soup.


6: Flat & Modular

For details, press DOWN arrow/key

For next rule, press RIGHT arrow/key

+++

A subject-oriented folder structure:

lib
  ├── config.js
  ├── crypto.js
  └── db.js
auth
  ├── splash.png
  ├── index.html.tmpl
  └── index.js
dashboard
  ├── index.html.tmpl
  └── index.js
server
  ├── websockets.js
  ├── routes.js
  └── index.js

+++

This has the advantage of locating related assets and/or modules near where they are used.


For details, press DOWN arrow/key For next rule, press RIGHT arrow/key

+++ Code can be argued over stylistic points. But cyclomatic complexity is a number representing (roughly) the # of branches in a block of code.


8: Immutable On Everything

For details, press DOWN arrow/key For next rule, press RIGHT arrow/key +++

Aim for immutablity. It's not much different.

const { Map } = require('immutable')
const map1 = Map({ a: 1, b: 2, c: 3 })
const map2 = map1.set('b', 50)
map1.get('b') // 2
map2.get('b') // 50

+++

It may be difficult at first adopting Immutable coding patterns. But it is well worth learning.


9: Pure & Stateless

For details, press DOWN arrow/key For next rule, press RIGHT arrow/key


Background: Over (roughly) the last 10 years, I've been gradually adopting & experimenting with these rules in my own projects. Some rules benefit all languages. Some rules only make sense for dynamic languages. And some of my rules are quite upsetting to some (and w/o JavaScript, I'd agree).

Create an issue/PR to help/disagree with me :)

Fin