Functional CM - Closures
Updated: Oct 12, 2022
Hey! How's it going?!
This is the first of (what I intend to be) a series of posts on my journey to improve the standard CM Language and add some extra flavors I enjoy on top of it.
If you're unfamiliar with CM Language, Configura, or even if you're new in the Dev world, worry not! I'll try to cover all the bases on the topics we are going to discuss, and I promise to try to provide working examples as much as possible so you can try all of the nice/weird things you will see here.
As the title of this post is already spoiled for you, we're going to start our journey by trying to add some Functional Programming flavors to our tasty CM bucket.
In computer science, functional programming is a programming paradigm where programs are constructed by applying and composing functions. It is a declarative programming paradigm in which function definitions are trees of expressions that each return a value, rather than a sequence of imperative statements which change the state of the program. - Wikipedia
As the first challenge, the goal is to add some infrastructure to allow us to write Closures in CM.
Two steps back
Before going any further, I want to ensure we are all on the same page here. I want you to enjoy this ride and fully comprehend the goal and its benefits once we reach it, so to ensure we will walk at the same pace, let me start by explaining using my universal language: Code.
For any upcoming content to make sense, as I previously stated, we need to cover all the bases regarding exactly what we're trying to do.
To illustrate the main concept we will work on; I think it's easier to show it in a way you can read and even try out.
We're exploring Partial Function Application (or just Partial Application) in the above code snippet. The goal here is to reduce the arity of a function by storing one (or more) of its parameters so that a nested function can use it as if it was part of itself.
The sum function above was created so that, when called, it will return not the value of a sum operation (as it clearly couldn't since it only takes one parameter) but another function with the provided value stored as its X value. This is called currying.
At line 13, we've created a function (sum5) that will take a parameter (Y) and add that to the cached value (5) and return the sum of both.
Try to guess the values that will be displayed for the operations performed for the calls starting at line 15 and look at the results below to check if you got them right:
line 15: 5 line 16: 10 line 18: 11 line 20: 6
This offers several possibilities to us as CM developers, the most interesting ones being:
Function Contract Simplification
We will cover each of these points in the next section.
Code Reusability and Contract Simplification
In CET, we often find ourselves in a situation where the only option to solve the problem is to copy a good portion of an existing implementation and change the bits we need elsewhere.
Although one could argue that there is always a way around - with various degrees of complexity - we know that day in day out, we face challenges that require a fast solution and, I can say that I've learned over time that's hard to point the finger at the developer without understanding the reasoning behind the final implementation.
Let's start looking at some CM code:
By looking at it, it's pretty obvious where some code redundancy is happening, right? We could improve this by storing the values for the icon and title and reading them from there when calling our log function. But, still, we would need to remember where that was stored (so we don't end up re-creating those everywhere we need to call this function) and, of course, pass that into the function when calling it.
We could also create a new function that does this for us, right? Something like:
I'm already feeling better. What about you?
There's nothing wrong with this approach. I think that this would be a very reasonable way to solve this particular problem. But (there's always a but), as I move along with my project, I might want to tweak things a little bit here and there.
Say I want to reutilize the Company Name, but I now want to display a dialogIcon.error instead of a dialogIcon.info. Or maybe I just want to append something to the title of this dialog.
I could go on creating new methods and optional/extra parameters for every combination of the inputs for the log method. Still, I think you already figured out that at the end, I'll just end up with a bunch of new methods that, sometimes, might be there just to cover a one-off and preserve code consistency.
What's the alternative?
The code above illustrates the use of closures inside CET, and it will also be valid once we reach the end of this series of posts.
The highlights we can extract from it are:
We still have only one code handling the modal creation (lines 5 through 8); therefore, still, only one place/codebase to maintain;
We get three different configuration paths from the curried function we've created. This allows us to effectively combine/configure the same code in several different ways (as shown in line 15 through 18) without introducing side-effects to the core logic;
What about that Lazy Evaluation thing?
Good question! It shows that you were paying attention.
The good news is that you've already learned about it without even realizing it.
The Lazy Evaluation aspect of our CM Closure can also be observed in the previous code snippet. By creating the function returned at line 3, we are storing the value passed into the log method via the icon parameter as being part of the scope of that function (and all of the functions that are part of it) without really executing (e.g., evaluating) the actual functionality (wrapped between lines 4 and 9).
The cool part of this is any time-intensive calculation that we add to be performed by our curried function can easily get memorized, and the already evaluated function scope remains stored. It can be accessed/shared to any nested function it might expose.
The execution of the wrapped code (lines 5 through 8) is conditioned to a call being made into the function generated at line 4. Then, and only then, we would have the wrapped logic evaluated and executed.
This blog would not exist without the help of those around me. Thanks to my lovely wife for the continuous support and to my friends Harmony and Tyler for taking the time to go through the site and help me polish things around here. Also last but definitely not least, a big thank you to Elizabeth Busch from Configura.
I hope you were able to learn something from this post, and hopefully, the examples were good enough to spark your curiosity to keep you hooked for the next step.
In the next post, we will start looking into CM's syntax extensions and how to create our very first custom syntax. Exciting, isn't it?!
Feel free to drop me a line if you have any questions on the topics being discussed, I'm always open to hearing what you think about the content on here. Also, make sure to sign up for the newsletter, so you are notified when new posts are available.
Stay safe, and see you next time!