Latest news about Bitcoin and all cryptocurrencies. Your daily crypto news habit.
When Procedural Is Better Than Declarative Programing
Procedural programming allows you to reason through complex processes, which can help you solve otherwise unapproachable problems, even if more work is required.
Recently, I started following a magician/puzzle channel on youtube. The channel author is Chris Ramsey, and his videos are surprisingly satisfying to watch. If youâre wondering what this has to do with procedural and declarative programming, I promise you weâll get to that, if you can just be patient for a minute.
Hereâs a couple different puzzles that Chris has solved on his channel:
Puzzle 1: The Impossible Bottle
Puzzle 2: Thorâs Hammer
So Whatâs The Difference?
I think puzzles are great metaphor for programming, that can help non-programmers understand the difficulty involved in programming. Often, we donât know how long something will take, or what the best approach will be, until we make an effort. Two different programmers can approach and solve the same challenge in very different ways.
Both of these puzzles are labelled as âimpossibleâ, but there is actually a very clear difference in the challenge involved with each. The impossible bottle allows you to clearly see what is going on, but the process of manipulating the parts and pieces is difficult to perform. If it werenât for the bottle blocking access, it would be trivial to solve.
With the âThorâs Hammerâ puzzle, exactly the opposite is true. Each of the pieces is accessible to touch and manipulate, but the relationship between them is hidden inside, and not visible to the programmer. . . i mean, puzzle solver.
Declarative Programming Hides Process, Reveals Relationships
There are many different forms of declarative programming. One of the most recognized forms of declarative programming is functional programming. Functional programming is simply describing the mathematical relationship between inputs and outputs. It relies heavily on ideas like recursion and composition.
functions are simply mathematical maps between inputs and outputs
One of the most common examples, to highlight the pros and cons of functional vs imperative programming, is computing the Fibonacci sequence.
Here is how you would compute the fibonacci sequence in Haskell:
fib :: Integer -> Integerfib 0 = 1fib 1 = 1fib n = fib (n-1) + fib (n-2)
This code came from this stack overflow question. Here is the description that was given to explain how it works:
So the infinite list of Fibonacci numbers can be calculated by prepending the elements 1 and 1 to the result of zipping the infinite list of Fibonacci numbers with the tail of the infinite list of Fibonacci numbers using the + operator.
On the surface, the Haskell code above looks quite simple, but you can see that there is actually a lot going on behind the scenes, in order to perform the computation efficiently. But we donât care about the process in this case, because the computer can figure out those details for us, and it is easier just to describe the relationships.
In the Haskell code above, we describe very specific things about the mathematical relationships involved. The type signature tells us this function takes an integer and returns another integer. Then we use a special haskell trick called pattern matching to describe the initial values for the function. From those values, we recursively describe how to compute further values.
Compare that to this C implementation of the fibonacci sequence IÂ wrote:
#include <stdio.h>int fibonacci(n){ int a = 1; int b = 1; int i; int tmp; for(i = 0; i < n - 1; ++i){ // swap a and b tmp = a; a = b; b = tmp; // add a and b a = b + a; } return a;}
int main(){ printf("\nfibonacci(0) = %d", fibonacci(0)); printf("\nfibonacci(1) = %d", fibonacci(1)); printf("\nfibonacci(2) = %d", fibonacci(2)); printf("\nfibonacci(3) = %d", fibonacci(3)); printf("\nfibonacci(4) = %d", fibonacci(4)); printf("\nfibonacci(5) = %d", fibonacci(5)); printf("\nfibonacci(6) = %d", fibonacci(6)); printf("\nfibonacci(10) = %d", fibonacci(10)); printf("\nfibonacci(20) = %d", fibonacci(20)); printf("\nfibonacci(40) = %d", fibonacci(40)); printf("\nfibonacci(50) = %d", fibonacci(50)); printf("\nfibonacci(100) = %d", fibonacci(100)); printf("\ndone"); printf("\n"); return 0;}
This code needs to be explicit about what values are stored, how many steps are involved in the loop, etc. It can be confusing because there are often minor tweaks to handle edge cases, or proper initialization. In this case, we have to loop n -1 times, and it may not be clear why. Also, if you run the code, you will see that eventually it gives the wrong answer, because with procedural programming the programmer is responsible for things like overflow, which we donât handle in this case. In haskell, the language will take care of that for us.
Declarative Programming is Great For Common Tasks Where You Need Help With The Details
Some may think that procedural and declarative programming are mutually exclusive, but usually, we mix the two approaches. One good example, is that even in procedural languages, it has become the norm to use declarative statements for memory management.
Think about the new keyword. This keyword exists in several languages, including, Java, C++, and Javascript, and it is actually mostly comparable in each one.
Here is an example from the online Java docs:
Point originOne = new Point(23, 94);Rectangle rectOne = new Rectangle(originOne, 100, 200);Rectangle rectTwo = new Rectangle(50, 100);
In C, malloc and free are used to manually allocate and unallocate memory on the heap. In these other languages, the new keyword handles a lot of that automatically for us. First of all, it determines what object we are going to be creating, and the size of memory required. Also, within the context of the expression, the new keyword indicates that the Object constructor should be called, with the provided parameters. Finally, an expression with new resolves to a value that can be used according to its object type. In the statement above it is used on the right hand of an assignment, but we could also call one of its methods anonymously, or pass it to another objectâs method.
Like the impossible bottle puzzle, declarative programming makes the details inaccessible, but makes it easy to see the relationships involved.
Procedural Is Better When You Care About The Steps
Recently, I was trying to write some complicated MySQL queries, for a web development task. SQL is a perfect example of how declarative programming includes more than just functional programming.
For this particular problem, I needed to perform computations to transform my data, using data from different tables in the database. SQL is great for querying and selecting datasets, but it isnât always the best choice for performing computations with that data.
Initially, I was using an imperative approach to process my data, on the server with django. But this proved to be too slow for my purposes, as the query would time out while the python interpreter was trying to perform the computations.
I then decided to move the computations into MySQL, to speed up the query time. While it looked like this would be fast enough, it soon became difficult to reason about the queries, steps, and the relationships involved. I found myself using loops and procedures in MySQL, which it supports, but isnât the ideal use case for the language.
Instead, I am going to use SQL queries to select the appropriate datasets, and then perform the computations on the client with javascript. I did this previously for other stats I needed to compute from the database, and I really like this approach because the computations are offloaded to the client, so itâs an efficient use of resources, and the page renders quickly as well. Additionally, javascript is a great language for working with datasets in a flexible way, because of its JSON support, even if the browser isnât the best environment for supporting mathematical or accounting computations.
Final Thought: Procedural Programming Gives You More Control, But Is it Worth It?
Our intrepid youtube puzzle solver, Chris Ramsey, decided to burn Thorâs hammer, instead of attempting to put it back together again, due to the sheer frustration and challenge involved in the task. But if he had kept track of his procedure for taking it apart, he could have simply reversed his steps to reassemble it. I think that is a good example of the benefit and drawbacks of procedural programming. Keeping track of all the steps involved, can allow you to perform more complex challenges, with greater control over the detail, but whether it is worth it is depends on the task, and whether it can be solved with a library or a different approach.
When Procedural Is Better Than Declarative was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.
Disclaimer
The views and opinions expressed in this article are solely those of the authors and do not reflect the views of Bitcoin Insider. Every investment and trading move involves risk - this is especially true for cryptocurrencies given their volatility. We strongly advise our readers to conduct their own research when making a decision.