∴ikura

Erlang beauty

There is an adage in the Erlang community that we should make our code
work, then make it beautiful, then, and if truly necessary, make it
all run fast. This insight is rendered from decades of coding of many
brilliant hackers. In fact, the last part is probably a nod to Donald
Knuth’s famous caution against early optimization.

It may look familiar to some; or maybe it’s new. Certainly, there are
variations of this in other languages for sure. But all the same, one
may coil up and ask, why is beautiful in the list?

Making code beautiful is essential, and there’s a reason it is parked
in the number-two spot pecking order. If our code isn’t beautiful,
it’s unlikely that it will convey just what the hell is going on.

Since I’m no level-ten Erlang poet, I consult a refactoring style
guide that ensures my code will be straightforward to read. A lot of
this guideline is lifted from Garrett
Smith’s
fantastic insight on the same matter — with some light re-purposing
here and there. If you need some tips on getting your Erlang code
past the mere working stage and into the beauty pageant, then please,
read on.

I call this module-writing heuristic ‘fucrs’ (pronounced any way you
like).

Give me an ‘F’ : function-ize

After a version of an Erlang module is working, I go back and turn all
my ‘case’ expressions into plain ol’ functions. For example:

foo(X) ->
case X of
bar -> void;
_Else -> undefined
end.

would become:

foo(X) -> foo1(X).
foo1(bar) -> void;
foo1(_Else) -> undefined.

This practice is a gold-standard for me, as I immediately noticed my
code to be vastly more lucid. ‘F’ also serves as a reminder to turn
any haphazard ‘funs’ into normal functions, too. Sometimes you need
‘funs’ — of course — but ordinarily, writing a plain function is a
much cleaner way to present things. Once done, I move on to the next
letter.

Gimme a ‘U’ : un-nest

This is an easy one to do, but sadly, it is not popular. It’s not
alarming to see code in the wild with functions entrenched six or
seven times. And incredibly, the Elixir language has introduced a
language devise, the pipe
operator, to
better mollify this malpractice. Obviously, nesting runs rampant. The
‘fucrs’ guideline takes an extreme conservative stance on this:
nesting should never happen, ever. The values passed into functions
must only be variables, or basic data constructs like lists, tuples,
etc.; never a function. For example:

foo(X) ->
final_function(maybe_function(X)).

would be re-written:

foo(X) ->
Maybe = maybe_function(X),
final_function(Maybe).

Spelling things out so pedantically makes code dead-simple & clear.
Yes, there is a tad more code, but you will also note that nothing is
hiding. Un-nesting simply dumbs things down. Now, who wouldn’t want
that after hours of squinting at a screen?

Gimme a ‘C’ : canonicalize

It’s nice to open a module and see some familiar faces. Although there
is no standard naming convention for functions, ‘fucrs’ makes an
attempt to include the ones from Garrett’s
talk
whenever possible. Functions named as ‘new,’ ‘start,’ ‘init,’ etc. are
pleasantly unsurprising and can help your modules have an air a
familiarity.

Gimme an ‘R’ : rename

Quite related to canonicalization, is to do a pass on the code in
order to rename variables/atoms as necessary. I find that in the heat
of the moment, a naming will occur out of some emotional response to
something. For example, I once caught an atom I had named ‘stupid_foo’
which at the time meant quite a bit, but means little thereafter.
Naming things can be hard, but I find when I am tasked with renaming
things in bulk, it’s easier than coming up with great names while also
tasked with getting code to work. Expressive names are helpful to your
future self, and others. Use simple, deliberate words as much as
possible.

Finally; ‘S’ : seven-ize

Someone smart, somewhere, at some time, did a study which concluded
that the human brain can easily hold six or seven items in short-term
memory with little trouble, but beyond that, it becomes taxing. This
applies nicely to lines of code in a function. Amazingly, after all
the above guidelines have been followed, bringing a function’s
lines-of-code count down to a maximum of seven is surprisingly easy to
do. It may seem crazy, but I challenge you to try it; first as an
exercise, then come to your own conclusions. For me, I tend to max-out
around five lines-of-code per function with no effort, though at
first, seven was an easier goal. The reason should be clear: a reader
can keep a handful of expressions in mind when reading such a terse
function; but reasoning a behemoth, it goes against what our brains
are capable of.

So, there you have it: a one-word style guide to consult after your
Erlang code is ticking along without error, but could benefit from a
face-lift. Give it a try, and be sure to thank Garrett if ‘fucrs’
works for you.