A brief delve into the presentation and performance of functional programs.
Introduction
I have been gradually learning Haskell for the past few months and I have been fascinated by how different it is from the imperative languages I am used to.
Here I document an experiment (which is based on one of my homework problems, I just thought it is a great example) that I did where we delve into comparing the presentation and the performance of code on a spectrum from imperative to functional.
Programming Challenge
Given the following theorem:
$$\begin{array}{r} \text{ Given integers }a,b \geq 1\text{ : } \\ \sum_{r_{1} + \ldots + r_{a} = b}\binom{b}{r_{1}}\ldots\binom{b}{r_{a}} = \binom{ab}{b} \end{array}$$Write a function that takes two integers a and b as input and evaluates both sides of the equation. Then use the function to verify that for every $A \in \lbrack 1,10\rbrack$ and $B \in \lbrack 1,10\rbrack$, the equation holds.
Definitions
First we will provide an explanation of what it means to be imperative and functional. (Disclaimer: this is my understanding of the concepts and may not be 100% accurate)
In imperative languages, you write a sequence of statements that change the state of the program, and use control flow elements like loops, conditionals, function calls and gotos to control the flow of the program. Essentially it is a sequence of explicit instructions executed in a explicit order.
In (pure) functional languages, you do not write sequence of statements nor do you modify the state of the program. Instead you build up your program by composing functions and values together to form new functions and values.
As a consequence you do not have control flow elements like loops, conditionals, function calls and gotos. Instead you use recursion, higher order functions (functions that transform other functions) and pattern matching to achieve the same effect.
Actual Code Samples
We will provide three samples that gradually changes from an imperative approach to a functional approach.
Pure Imperative Approach
This is a nothing-special imperative approach to solve this problem. Notice the frequent use of loops, conditionals and mutable states.
| |
Mixed Approach
A lot of modern languages are multi-paradigm, meaning they have both facilities for imperative and functional programming. Rust is one of them. In this example, we use a mix of imperative and functional programming but substituting array iterations with higher order functions.
| |
Pure Functional Approach
We write this program in Haskell, a purely functional programming language. The only non-functional part of the program is computing the CPU time. I wrote comments to assist in understanding the code and the features of functional programming used in the code.
| |
Just to be Fair
Just to be fair to haskell we use another GC-ed language (Kotlin/JVM) as control.
| |
Performance
We measured two metrics for each code sample:
The number of non-empty non-comment lines in the code.
The time it takes to finish the verification.
| |
It seems that haskell is significantly more concise and comparable to Kotlin/JVM in terms of performance. Non GC-ed languages like rust are significantly faster than both GC-ed languages.
Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.