Glossary

Terms and concepts explained

Data Type#

The "data type" of a function is the last argument's type. For example

UtilityData Type
addnumber
appendOnearray
mapValuesCollection

To explain, let's use mapValues to scream some words

const scream = someString => someString.toUpperCase()
const screamAll = mapValues(scream)
//                          ^
//                    first argument

const words = ['hey', 'yo']
const screamedWords = screamAll(words)
//                              ^
//                        last argument
console.log(screamedWords)
const scream = (someString: string) => someString.toUpperCase()
const screamAll = mapValues(scream)
//                          ^
//                    first argument

const words = ['hey', 'yo']
const screamedWords = screamAll(words)
//                              ^
//                        last argument
console.log(screamedWords)
const scream = someString =>
  someString.toUpperCase()
const screamAll = mapValues(scream)
//                          ^
//                    first argument

const words = ['hey', 'yo']
const screamedWords = screamAll(words)
//                              ^
//                        last argument
console.log(screamedWords)
const scream = (someString: string) =>
  someString.toUpperCase()
const screamAll = mapValues(scream)
//                          ^
//                    first argument

const words = ['hey', 'yo']
const screamedWords = screamAll(words)
//                              ^
//                        last argument
console.log(screamedWords)

In Common FP, I consider scream to be the first argument to mapValues and words the last. Let's reorder the code to better see what I mean:

const scream = someString => someString.toUpperCase()
const words = ['hey', 'yo']

const screamedWords = mapValues(scream)(words)
//                              ^       ^
//                            first    last
console.log(screamedWords)
const scream = (someString: string) => someString.toUpperCase()
const words = ['hey', 'yo']

const screamedWords = mapValues(scream)(words)
//                              ^       ^
//                            first    last
console.log(screamedWords)
const scream = someString =>
  someString.toUpperCase()
const words = ['hey', 'yo']

const screamedWords = mapValues(scream)(words)
//                              ^       ^
//                            first    last
console.log(screamedWords)
const scream = (someString: string) =>
  someString.toUpperCase()
const words = ['hey', 'yo']

const screamedWords = mapValues(scream)(words)
//                              ^       ^
//                            first    last
console.log(screamedWords)

Though I don't suggest writing code that way, as it's harder to understand

Why is the Data Type Important?

In functional programming, we place the "data argument" last to allow for composable functions. By labeling each utility with its data type, you can more easily see how to compose them.

Side Effects#

What is a side effect?

A side effect is code that modifies state outside the current function. For example, sort and reverse cause side effects since they mutate their array. This is different from toSorted and toReversed, which return new arrays and are thus free from side effects.

Why are they important?

From a functional programming (FP) perspective, side effects complicate code. For example, we must take care when calling sort since the array may be referenced elsewhere. This care is not necessary for toSorted since the original array stays intact.

FP utilities are free from side effects by default, with the goal of simplifying code.

When are side effects fine?

I don't mean to portray side effects as bad. Often they're necessary. For example, we cause a side effect any time we update data in storage, such as a user preference.

They can also be the better solution. For instance, we wouldn't call toReversed over an enormous array since the copy may bloat memory usage.

Other times we don't have a choice since a library requires it. One common example is the node library Express, where side effects are how you interact with it.

There are plenty more, but I'll leave it at that.

Mutating Data#

All utilities that mutate data are prefixed "m".

What does mutate mean?

It just means change. For instance, we can mutate an array using push or an object using assign.

Sorry if this term feels foreign. I'd use a more familiar word, but this concept is important.

What is data?

In Common FP, data refers to the last argument a utility takes. For example, with mAppendAll, it's base

Can everything be mutated?

No, in JavaScript we can't mutate primitives, e.g., strings and numbers.

Why is mutating important?

In functional programming, we avoid mutating since it's a side effect. See that section for why we avoid side effects.

By default, Common FP returns shallow copies of its data rather than mutating it, with the goal of simplifying code.

Then why add utilities that mutate data?

Sometimes we don't have a choice. Libraries may require us to mutate properties, or performance concerns may prevent us from making shallow copies. There are many use cases for mutating data, and Common FP helps you do so in a functional way.

Async Utilities#

All async utilities are prefixed "p" as in "promise".

What is an async utility?

It's a utility that takes an async function. For example, pAny takes an async predicate to determine if any entry passes a condition. This allows us to do things like call an API for each item in a shopping cart.

Why aren't concurrency limits supported?

I want to keep Common FP simple, and variable concurrency complicates the code. Devs can always depend on Caolan's async, which should support most cases. Just be aware it generally returns arrays rather than the collection type passed in.

We can revisit support if there's enough demand.