Lexical functional programming — jargon and naming convention

Based on Straal’s JavaScripters style guide

Aleksander Kućma
Daftcode Blog

--

This article was originally published on Straal company blog.

“Everything flows” — Heraclitus

The reactive paradigm followers like to speak “everything is a stream”.
The phrase attributed to Heraclitus is valid for both functional and reactive paradigms. In the first one, we have pipelines, in the second one streams. That testifies to the significant similarity between these paradigms and shows that reactive programming (but more FRP) is, in fact, an asynchronous implementation of a functional flow.

There are many studies on the subject of functional programming. However, finding noteworthy resources about naming or specific inspiring conventions is quite a challenge. It requires a healthy dose of creativity and often turns out to be far more complicated than it might seem at first glance.

About Straal

Straal — one of the Daftcode’s venture — is an international provider of payment, optimization and fraud prevention solutions for future-minded businesses. The company offers a comprehensive suite of products that make accepting digital payments easier, as well as more effective and secure.

Straal enables accepting one-off and recurring payments carried out by customers with credit and debit cards of all major organizations, initiating transactions via open banking APIs, ordering SEPA Direct Debit cycles and more. Thanks to Straal, customers can pay in currencies of their choice (over 150 options), using their preferred desktop and mobile platforms, while merchants can effectively maximise their transaction authorization rate and mitigate risk.

How to standardize descriptive names?

“Good programmers give functions descriptive names so that when we see the code, we can see the function names and understand what the function does.”
Eric Elliott, “Master the JavaScript Interview: What is a Pure Function?”

In essence, I’ve observed that many programmers have a problem with descriptive names. The style guide that my colleagues from Straal and I have created is a fulfillment of our experience and expectations, presenting a ready set of good practices and restrictions in our projects. It makes our code reviews much easier and effectively tackles issues such as naming dilemmas.

The article that you read is an analysis of our style guide, and you can find it on our GitHub repository. Besides, we apply strict ESLint rules, which are complementary to this style guide.

Why “Lexical functional”?

“Composition is a powerful tool for abstraction that transforms imperative code into more readable declarative code — Kyle Simpson, “Functional-Light JavaScript”

The declarative composition is essential for understanding what FP is, but we’ll go one step ahead and think about the naming structure in our functions.

The inspiration for the title is “Lexical functional grammar”, which provides a specific grammar framework to focus on the syntax of multiple dimensions of the linguistic structure.

“Functional” is something more than just a common phrase — it’s a connection of both ideas to focus on specific problems for code writing. In this article, you will see the jargon and structures to the naming convention in the FP-inspired world. Sometimes I use specific FP-terms in brackets to provoke your curiosity or wink knowingly at those already familiar with them.

Constants only

const foo = ‘foo’

It’s worth pointing out that constants are the strictest declaration. This is the first step in avoiding mutations because using other variable declaration can result in side effects appearing quickly.

For boolean values describing a state of something we mostly use the following convention:

const hasFoo: Boolean
const shouldFoo: Boolean
const barDisabled: Boolean
const barEnabled: Boolean
const isDisabled: Boolean

As you see we have the following scheme for the functions with a specific role (I describe this part in detail in further sections):

const fooChecker: Function
const fooValidator: Function
const fooExtractor: Function
const fooHandler: Function
const fooCallback: Function
const fooFallback: Function

Why do we use const operators in this case? Because it provides the “temporal dead zone” and helps to take care of a correct order in the code.

Operations and operator functions

const reduced = reduce(collection, reducer, accumulator)

The above scheme could be a base for most of your problems in a naming dilemma. As you see, our composition includes target collection (also known as a functor because it is mappable), reducer (an operator function), and optional argument — in this case “accumulator”.

Let’s start from “reduce” — the word is a verb which indicates an action, so it is an operation — a declaration with defined scopes per argument with all the information on what that function does.

We also have an operator called “reducer” (or “iteratee”) — a noun, which means it’s a role that provides functionality to realize “reduce” operation, we called it operator function.

“Reduced” is an adjective and indicates that something has been completed — it is a result of our process.

Let’s check implementations of reduction in Lodash and vanilla JS — as you can see, that that terminology is nothing innovative and you have probably seen it before. Now it’s time to use it more consciously and look for analogies in your code.

For a more “lexical” way, we can use it in our code as a suffix — then you always know what the role of each of the individual variables is:

const fooReducer = (result: Any, current: Any) => Anyconst fooReduced = reduce(
collection: Any,
fooReducer,
result,
)
const fooMapper = () => Anyconst fooMapped = map(
collection: Any,
fooMapper,
)

In the sort functions (“union”, “sort”), the invoke function per element is called a comparator. For “filter”, “some”, “find”, and similar operations (using probably pure functions) we use predicate function called checker or just predicate:

const fooChecker = () => Anyconst fooFiltered = filter(
collection: Any,
fooChecker,
)

In the next part of the article, you can see we use that convention totally in all cases.

Pipelines

Pipelines are a handy thing to a flow between functions. It’s an essence of functional programming.

Pipe (or compose in the opposite direction) as a functional pipeline:

const fooFlow = pipe(
bar: Function,
baz: Function,
qux: Function,
)

Pipe operator in a reactive stream:

const foo$ = bar$.pipe(
filter(barComparator: () => Boolean),
pluck(‘bar’),
)

Both ways - reactive and functional use the same concept of a pipe. The main difference is, of course, the approach to synchronicity.

Curry and partial

Forget about “this”, “apply”, and “call” methods. Everything you need is a flat description of flow and proper declarations that express your control flow with the partial transmission of arguments:

const curriedReduce = curry(reduce)(collection, reducer, accumulator)

We have extended the example of reducing called “curriedReduce” (we can also call it “reduction”) — it means that you have to execute that function to end the process. Pay attention to vocabulary — call your curried functions “curriedSomething” or as an ongoing process (like in this case, “reduction”).

We can also create curried setters with predefined arguments or change the order of execution and use partial. The result of the implementation of both functions will be the same:

const curriedFooSetter = curry(obj: Object, value: Any) => ({...fooObject, value})const barSetter = (obj: Object, value: Any) => {barObject, ...value}const setFooToObj = curriedFooSetter(fooObject: Object)const setBarToObj = partial(barSetter: Function, barObject: Object)

For a simple merging, “spread operators” will be enough. For more involved, you should remember about avoiding mutations on your reference, use Lenses, or immutable library like Immutable.js.

The analogic situation with getter functions:

const curriedFooGetter = curry((fooObject: Object, value: Any) =>    fooFormatter(get(fooObject, ‘value’, {})))const barGetter = (barObject: Object, value: Any) => barFormatter(get(barObject, ‘value’, {}))const getFooFromObj = curriedFooGetter(fooObject: Object)const getBarFromObj = partial(barGetter: Function, barObject: Object)

Avoid if/else

Instead of them, you can use conditional (ternary) operators and cond functions. That increases clarity, and helps to avoid imperative chaos in your code.

Conditional (ternary) operators — the condition can be a predicate (returns true or false) or boolean (e.g. hasFoo):

return foo(value)
? bar(value)
? qux()
: quux()
: baz()

A better choice for more complicated cases:

import L from ‘lodash’const bar = (value) => value ? qux() : quux()const sthCond = L.cond([
[foo, bar],
[L.stubTrue, baz]
])
return sthCond(value)

Objects

We have three typical use cases: dictionaries, switches, and factories.

Dictionaries

We use dictionaries just like in other languages to keep key/values:

const getValue = (type) => {   const fooDict = {
bar: ‘bar value’,
baz: ‘baz value’,
qux: ‘qux value’,
}
return fooMap[type]}

Switches

In most cases, we can replace typical JS Switch (with break/return) by cond operation function. But in this case, any conditional will be executed if none of them comes true. In some extreme situations, getting a specific function by a key value is a better choice. The following code shows behaviour similar to a classical switch.

const fooGetter = curry((fooObject: Object, value: Any) => fooFormatter(get(fooObject, value, {})))const barGetter = (barObject: Object, value: Any) =>    barFormatter(get(barObject, value, {}))const makeAction = (action: Any) => {   const bazSwitch = {
foo: fooGetter(fooObject: Object),
bar: partial(barGetter: Function, barObject: Object),
}
const actionGetter = get(bazSwitch, action, constant(null)) return actionGetter()}

Factories

We use neither classes nor constructors. Factories are functions that create objects of any kind— singleton or instances. The term “middleware” is used to define the factory function with partial arguments, e.g., initial options. Our implementation of Factory is quite specific — we don’t use `this` (to use more explicit contexts and for more FP-friendly style), so you can’t chain methods, but you can use the pipeline:

const someFactory = function({store, fetcher}, watcher){
const fetch = fetcher(store)
const watch = watcher(store)
return {
fetch,
watch
}
}const store: Objectconst fooWatcher: Function
const barWatcher: Function
const someFetcher: Function
const someMiddleware = curry(someFactory)({store, fetcher: someFetcher})
const foo = someMiddleware(fooWatcher)
const bar = someMiddleware(barWatcher)
const { watch: fooWatch, fetch: fooFetch} = foo
const fooPipe = pipe(fooWatch, fooFetch)
const { watch: barWatch, fetch: barFetch} = bar
const barPipe = pipe(barWatch, barFetch)

Reactive streams

It’s customary to use a “$” suffix in streams. We distinguish two types of observables: hot and cold — everything will be more apparent with the rules below.

Hot observables:

Hot observables are independent of subscribers — it can emit some changes without an observer. In this case, we use suffix ‘Dispatcher’ because we use them like a singleton — for one event, we have only one declaration on the whole code and reuse it by importing:

export const fooDispatcher$ = fromEvent(window, ‘foo’)

Cold observables:

In any other case, the observable will be cold:

const fooObserver = => Anyconst fooSubscription = subscribe(
foo$: Observable$,
fooObserver,
)
fooSubscription.unsubscribe()

Stream creators:

For functions that create a new observable:

import { throwError } from 'rxjs';export const fooStreamCreator = (): Observable<TBar | never> => {   return condition
? throwError(new Error(‘Foo error’))
: bar$
}

Summary

I know that the topic has not been exhausted, yet hope that the analysis of tools and approaches will be helpful and useful for you. I hope that you enjoy the presented proposition of a solution for a common problem of many teams — a naming inconsistency in code. If you have any ideas for improvements, feel free to leave me a comment.

If you have seen that you can improve some of this, I’m waiting for your comment, or you can think about your own “lexical guide”.

Spread the word!

What’s next?

For more information about FP I can recommend these lectures:

If you enjoyed this post, please hit the clap button below 👏👏👏

You can also follow us on Facebook, Instagram, Twitter and LinkedIn.

--

--