What is functional programming?

mortoray

edA‑qa mort‑ora‑y

Posted on May 25, 2017

What is functional programming?

Three key concepts comprise the essence of functional programming: first-order functions, pure functions, and immutable data. Together they express a fundamental architectural paradigm on how units of code interact with each other. While there are numerous languages that specialize in this paradigm, it's gained wide acceptance and can be used in any general purpose language.

Why?

The value in functional programming is in its ability to express transformations of data at a high-level. We don't need to get into the details of how a computer works. A programmer gets to think primarily in the domain of data and calculations.

Functional programming creates a stable system. The code has no ability to corrupt the environment in which it runs. It is predictable and deterministic. Given the constraints on the paradigm, a compiler has significant leeway to optimize and create efficient executables.

Pure functions are essentially the gold standard when it comes to calculations. They are easy to understand, having a single purpose and clear input to output mapping. This makes them easy to test, use and maintain.

This article focuses on the benefits and core qualities of functional programming. As with all paradigms, there are limitations. I'll have to look at those in a future article.

Pure functions

A function is pure if the result is based solely on its input arguments and it doesn't cause any side-effects. Let's look at these two points.

Evaluates from arguments

The result of a pure function is always the same when given the same input arguments. There is no hidden state that could modify the result of the values.

var total = sum( collection )
var C = matrix_mult( A, B )
var safe_name = encode_uri_comp( user_name )
Enter fullscreen mode Exit fullscreen mode

Given a particular collection the total will always have the same value. matrix_mult will behave like the mathemetical definition, giving the single valid result for the multiplication. A standard dictates the transform done by encode_uri_comp, and it will always be the same.

This is a strong contrast from functions that involve implicit or hidden state.

var value = random( 100 )
var time = get_current_time()
var next = pop( stack )
Enter fullscreen mode Exit fullscreen mode

The result of these functions will not be the same when called again. The result of random is based on its argument, it will return a value from 0 to 100. It won't, however, return the same value each time, it's relying on a hidden state to return unpredictable values. get_current_time doesn't even accept arguments yet will produce a new value each time. pop only depend on its arguments, but since it modifies the stack it won't return the same value next time.

Side-effect free

A pure function does not create side-effects. This means the state of the system is the same before and after the call. Take a look at a few counter examples, these functions produce side-effects:

print( "Hello" )
stack.push( value )
var handle = open( "file.txt", write )
Enter fullscreen mode Exit fullscreen mode

These change the user's console, an in-memory data structure, or the filesystem. They result in a change in the state of the system thus are not pure functions. Changes in state will alter the results of impure functions.

We only care about observable side-effects. There are several state changes that we ignore for practical reasons. A function can have an internal cache for efficiency. The cache doesn't influence the result, therefore it isn't an observable side-effect. Calculations take time, but we don't consider the change in system time a significant effect. We're just looking at the high-level input/output of functions, and how the user perceives the system.

We ignore these side-effects for the purpose of our definition and compiler implementation. There are of course domains where the impact of memory allocation and time elapsed cannot be ignored.

Immutable data

Related to the pure aspect is the immutable data. This refers to data structures that get a value when created and can never be changed.

var a = point(4,5)
var b = a.normal()
Enter fullscreen mode Exit fullscreen mode

The value of a will always be 4,5, it cannot change. normal creates a new point as it's return value. The functional paradigm focuses on the transformation of data into new forms without modifying the input data.

There is no difference between assign-by-copy or assign-by-value with immutable data. Since the source value can never be changed it doesn't matter how the assignment is done. This gives the compiler room to optimize, but it also minimizes the thought overhead when reading the code.

Immutables can also improve thread-safety. Once a thread has a reference to an object it no longer needs to worry about synchronization, since that object won't change. This also minimizes ordering concerns in single-threaded code. We don't have to worry about the current state of an object since it has only one state.

Immutable data and side-effect free functions can almost be considered the same feature. You can't effectively use just one of them. They work best together.

First-order functions

Functional programming treats functions as variables just the same as data. In particular, a function can be passed as argument to another function. This is a form of generic programming that helps reduce code redundancy.

var sum = fold( numbers, add )
var product = fold( numbers, mul )
Enter fullscreen mode Exit fullscreen mode

fold is a common functional concept that inserts an operator between all the items in a list and evaluates the result. Here we pass it add to perform a summation and mul to get the product.

var compare = is_reverse ? greater_than | less_than
var abc = sort( names, compare )
Enter fullscreen mode Exit fullscreen mode

This approach binds the function to a named variable for use later in the compare function.

Lambdas and Closures

Languages with functional features also provide for lambda functions, and closures. Those are technically two different concepts but tend to be exposed within the same feature.

A lambda function is an anonymous function. These are often used as arguments.

var ordered = sort( products, (a,b) -> { 
    return a.SKU < b.SKU
})
Enter fullscreen mode Exit fullscreen mode

A closure goes a step beyond by allowing the use of variables outside the scope of function. Though often combined with lambdas, they need not be.

var is_reverse = get_config( "product_reverse" )

var compare = (x,y)-> {
    return is_ordered ? x.SKU < b.SKU | b.SKU < a.SKU
}

var sorted = sort( products, compare )
Enter fullscreen mode Exit fullscreen mode

In this example, the compare function binds to the is_ordered variable defined in the parent scope.

Expressiveness

Functional programming is a valuable tool when implementing algorithms. Pure functions and immutable data make it very appealing in terms of safety as well as compiler optimizations. First order functions introduce an expressive way to reuse concepts and limit code redundancy.

It is often regarded as a pure computational paradigm. It lets us work in the domain of algorithms without being burdened by machine details. This singular focus can improve productivity and clarity.

Wherever the functional paradigm is possible to use, it tends to be the preferred solution. For a complete program, it blends naturally with imperative and reactive programming.

💖 💪 🙅 🚩
mortoray
edA‑qa mort‑ora‑y

Posted on May 25, 2017

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

What is functional programming?
programming What is functional programming?

May 25, 2017