Chapter 02: First Class Function

A Quick Review

When we say functions are "first class", we mean they are just like everyone else... so in other words a normal class. We can treat functions like any other data type and there is nothing particularly special about them - they may be stored in arrays, passed around as function parameters, assigned to variables, and what have you.

That is Python 101, but worth mentioning since a quick code search on github will reveal the collective evasion, or perhaps widespread ignorance of this concept. Shall we go for a feigned example? We shall.

hi = lambda name: print(f"Hi {name}")
greeting = lambda name: hi(name)

Here, the function wrapper around hi in greeting is completely redundant. Why? Because functions are callable in Python. When hi has the () at the end it will run and return a value. When it does not, it simply returns the function stored in the variable. Just to be sure, have a look yourself:

hi # <function __main__.<lambda>(name)>
hi("jonas") # "Hi jonas"

Since greeting is merely in turn calling hi with the very same argument, we could simply write:

greeting = hi
greeting("times") # "Hi times"

In other words, hi is already a function that expects one argument, why place another function around it that simply calls hi with the same bloody argument? It doesn't make any damn sense. It's like donning your heaviest parka in the dead of July just to blast the air and demand an ice lolly.

It is obnoxiously verbose and, as it happens, bad practice to surround a function with another function merely to delay evaluation (we'll see why in a moment, but it has to do with maintenance)

A solid understanding of this is critical before moving on, so let's examine a few more fun examples excavated from some libraries.

class BlogController:
def index(self, posts):
return Views.index(posts)
def show(self, posts):
return Views.show(post)
def create(self, attrs):
return Db.create(attrs)
def update(self, attrs):
return Db.update(post, attrs)
def destroy(post):
return Db.destroy(post)

This ridiculous controller is 99% fluff. We could either rewrite it as:

class BlogController:
index = Views.index
show = Views.show
create = Db.create
update = Db.update
destroy = Db.destroy

... or scrap it altogether since it does nothing more than just bundle our Views and Db together.

Why Favor First Class?

Okay, let's get down to the reasons to favor first class functions. As we saw in the BlogController examples, it's easy to add layers of indirection that provide no added value and only increase the amount of redundant code to maintain and search through.

In addition, if such a needlessly wrapped function must be changed, we must also need to change our wrapper function as well.

http_get('/post/2', lambda json: render_post(json))

If http_get were to change to send a possible err, we would need to go back and change the "glue".

# go back to every http_get call in the application and explicitly pass err along.
http_get('/post/2', lambda json, err: render_post(json, err))

Had we written it as a first class function, much less would need to change:

# rende_post is called from within http_get with however many arguments it wants
http_get('/post/2', rende_post)

Besides the removal of unnecessary functions, we must name and reference arguments. Names are a bit of an issue, you see. We have potential misnomers - especially as the codebase ages and requirements change.

Having multiple names for the same concept is a common source of confusion in projects. There is also the issue of generic code. For instance, these two functions do exactly the same thing, but one feels infinitely more general and reusable:

# specific to our current blog
valid_articles = lambda articles: (a for a in articles if a is not None)
# vastly more relevant for future projects
compact = lambda xs: (x for x in xs if x is not None)

By using specific naming, we've seemingly tied ourselves to specific data (in this case articles). This happens quite a bit and is a source of much reinvention.

I must mention that, just like with Object-Oriented code, you must be aware of self coming to bite you in the jugular. I avoid using self like a dirty nappy. There's really no need when writing functional code. However, when interfacing with other libraries, you might have to acquiesce to the mad world around us.

Some will argue that self is necessary for optimizing speed. If you are the micro-optimization sort, please close this book. If you cannot get your money back, perhaps you can exchange it for something more fiddly.

And with that, we're ready to move on.

Chapter 03: Pure Happiness with Pure Functions