Expressions as arguments

Expressions as arguments are executed before function is called. Yes …, and ?

And that is it. Keep that in mind. Don’t do it if you don’t have to. Often beginners are caught in this visible trap, and sometimes even seasoned JS minor (or major) celebrities are caught with it. As a beginner you feel this is one of those “obvious” things no one bothered to mention explicitly to you. Perhaps because it is “obvious”, right ? JS is one very simple language which always can make you feel you have somehow lost yourself in very complex part of the woods.

The other day I was trying to find some good example for my dbj.cond() project. With standard dbj.cond() one can get carried away and quickly produce a lot of “elegant” and simple code, solving some complex logic behind. For example:

Much cleaner than if/else cascade that would be otherwise required. (as described in the comments on the right). Very elegant indeed ?

But herein lies the trap

As with any other JS function call, all the expressions placed as arguments will be executed before the function is called. In JavaScript (and many other) “C like” languages arguments to the functions if expressions, are transformed into the values. If JS interpreter meets the expression in the function call, it simply executes it in place, and then pases the resulting value as an argument. Thus the dbj.cond() call above will be effectively first replaced with:

Thus each expression is executed in-place, and just then the function is called (in this case dbj.cond()) with resulting values as arguments. This obviously makes this snippet, not suitable for the title of “elegant coding”. One can easily imagine much more complex expressions as arguments, instead of simple JS comparisons. The simple rule is:

Avoid placing expressions as function arguments.

Always think first, if and how that can be avoided. If you just “sit and type” sprinkling this kind of “elegance” around, then I am afraid, you will be left with just bad JS code. And very often under the attack of some quiet and nasty bug, in that complex part of JS woods.

But. It has to be said, this syndrome can not be 100% avoided. So what can we do if we really, really have this “more complex requirements” to solve ? (and the real one is approaching )

Let me use the above snippet and some more, to gradually develop few solutions. For a starter instead of expressions as arguments, simply use good old anonymous functions:

This is better. Better, but not ideal. Still above call will first execute all the expression acting as check values, before actually going into the dbj.cond(). Going further, we need to present some real coding tasks to better understand the reasoning behind the solution.

To solution through Recursion

This syndrome can be especially hard hitting if one does recursive programming. Instead of some obscure example (invented by me), here is one very well known recursion example, found in a tutorial about LISP (cond …). Now, instead of “running for the hills”, faced with LISP, please observe with me, this little beauty:

It looks really simple, does it not? Everything is coded as a single (cond …) call. When x is 1, return the number of steps required to terminate the recursion. If x is odd, call itself recursively with “triple plus one”, and the default cond action is to call hotpo() recursively with “half”. And that is it. OK. Are you ready yet with your JS coded solution ? You are? Fine. Instead of potentially patronising you because of your initial solution I will present my solution first.

So, the task at hand is to refactor the above LISP into the JavaScript. To transform this to JS the same technique from above, can be used. Also this time to mix dbj.cond() and recursive solution. Here is that example transformed into the JS with the help of dbj.cond() :

What are we actually doing here is called “passing expressions as lambdas”. Why are we doing this? You already know why.

You know now that if we would just leave expressions as arguments, they will be executed immediately and before dbj.cond() is called. Also provoking a stack overflow, since the argument hotpo(…) will be called, first, before going into the dbj.cond(). Still in 90% of cases, people think of this as the initial and “obvious” solution:

To avoid this stack overflow, I had to apply our previous solution and wrap the expressions in the anonymous functions (sometimes known as “lambdas”). dbj.cond() returns the outcome pair of its check value. Since it is a function, we simply call it, in place . And that is it. Is this then end of story then … ? No it is not.

Yes, there is more

In a typical JS spirit, there is always yet another, usually mind bending variant to any solution. In this case I do not like the shape of the above code. So, I will do the following, with a little help of (gasp!) eval():

What is going on here? This is now, indeed much closer to the original LISP. This works because eval () is executed in the context of the caller, which is hotpo(). Some (LISP heads) might even like this and call it “solution with true lambdas”. Well, this might be one of those badly needed examples to prove that eval is not always evil. So, why not “go all the way” and implement a version of dbj.cond that will receive only “pure lambdas” in strings, as arguments and will eval() each one of them. But not all of them. Just the ones needed. Here we go:

Yes. dbj.condeval() can receive any type of args, not just strings. Internal eval’s will be called with the single values passed as arguments. Note: eval() argument does not need to be of a string type. Check if you do not believe me.

It seems, at least to me, if used judiciously, all of this might be useful. I also can think of the above string arguments passing, as indeed passing “pure lambdas”. The interesting result is that no arguments as expressions are executed before needed. They are simply wrapped in a strings acting like simple JS lambdas. We are thus, at last, “passing expressions as lambdas”, too. Just like LISP does.

Does this utility has an limited scope of usability? It sure does. Will I use it? I sure will. Enjoy … or dislike.