Updated: 3 days ago
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 not familiar 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 all of 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 in 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 right into 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
More specifically, as the very first challenge, the goal here is to add some infrastructure in place to allow us to write Closures in CM.
Two steps back
Before going any further I want to make sure 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 make sure we will walk at the same pace, let me start by explaining using my personal universal language: Code.
In order for any of the upcoming content to make sense, as I previously stated, we need to cover all the bases regarding what exactly is that we're trying to do.
To illustrate the main concept we are going to work on I think it's just easier to show it in a way that you can read and even try it out.
On the above code snippet, we're exploring something called Partial Function Application (or just Partial Application). The goal here is to reduce the arity of a function by storing one (or more) of its parameters in a way that a nested function can make use of it as if it was part of itself.
The sum function above was created in a way 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 belowto 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 a problem is to copy a good portion of an existing implementation and change the bits we need somewhere else.
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 a little bit by storing the values for icon and title somewhere and reading it 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. In fact, 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 but, 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 from 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 here is that you've already learned about it without even realizing it.
The Lazy Evaluation aspect of our CM Closure can be observed on the previous code snippet as well. 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 really cool part of this is, any time-intensive calculation that we add to be performed by our curried function can easily get memoized and the already evaluated function scope remains stored and 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 being evaluated and executed.
This blog would not exist were it not for the help of those around me. Huge thanks to my lovely wife for the continuous support and also to the friend's 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 the new posts are available.
Stay safe and see you next time!