JavaScript : function arguments are expressions

Generic Lambda

Consider this statement.

Besides, reminding us of desperately bad parts of JavaScript (mixed with DOM), what else happens here?

That whole call to fun() is executed before the fun() is called. And then the fun() is called with the results of all the expressions executed in its call. For the above javascript interpreter does something like this:

Each and every expression in that call is executed first. This is the “call expression” part that is executed first: r1, r2. Whatever r1 and r2 happen to be. And they happen to be functions right now. Thus r1 and r2 will be executed, two alerts will popup and the r1() and r2() results will be saved on the call stack, and just then fun( result_of_r1, result_of_r2 ) will be called. Completely useless but legal javascript. Just to convincingly show the issue. And the issue we will discuss next. But first a slight detour.

The LISP. Gasp!

Yes, the deep end of the pool. Stay with us.  LISP is basically extremely simple but (to you) strange syntax. We shall gain the ground quickly.

This “expressions in a function call” 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 this tutorial about LISP (cond …).

The cond construct in LISP is used for branching, You know it as if/else cascades in JavaScript. LISP is more elegant. The cond function syntax:

If the test evaluates to true, the action is evaluated. And then the whole cond branching stops there. That is from circa 1956, and still more readable than if/else cascades we got used too in “C like” languages.

How is this helping me?

We shall discuss next how is this helping us and why. Each clause within the cond call statement consists of pairs: conditional test and an action to be performed. Now, instead of “running for the hills”, faced with LISP, please observe with me, this little beauty. We shall transpose this into the JavaScript.

It looks straightforward, 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. Simple but serious example from computer science history.

Back to the safety of JavaScript

The task for you: write the above LISP in JavaScript

Are you ready yet with your solution of the above? You are not? That is fine. Instead of potentially patronising you because of your initial solution, I will present my line of thinking 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. Let me first emulate LISP cond in javascript

The cond()

Nice simple and easy. For details please consult your older ECMA Script textbooks now.

Besides coding the above in javascript this also is the time to mix dbj.cond() (an javascript) and recursive solution, as the hotpo() requires. Here is that example transformed into the JS with the help of dbj.cond() :

The key question is: Why I have done the action arguments this way?

What are we actually doing here is called “passing expressions as lambdas”. Why are we doing this? You already know why. We do not want all the expression executed at the time of dbj.cond() function call. Those actions as functions will be returned as functions, not executed.

In contrast to JavaScript, LISP and its cond function too is not executing the whole cond() call at all.  It does evaluation as it meets the next thing to be evaluated. Thus it stops at the first pair that satisfies the condition and the rest is not evaluated. Hence the term “short-circuiting” coined by the LISP inventor McCarthy.

Now let us see your contribution.

Javascript is not LISP

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().

I am sure you are not in that group, but I have to report, still, in 90% of cases, people think of this as the initial and “obvious” solution to the LISP hotpo():

To avoid that stack overflow, I had to wrap the expressions in the anonymous functions (sometimes known as “lambdas”). dbj.cond() returns the action pair if its test value is true. Since it is a function that is returned, we simply call it, in place. As you did too.

And that is it. We have shown a simple but non-trivial real-life use case and a simple solution.

Is this the end of the story then …? No, it is not.

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? It this forbidden JavaScript?  I beg to differ. 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() function. eval() immediately executes the code passed to it as simple strings.  And voila. Elegant solution.

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.

What is the point of all of this?

The point number one: You must be good in abstractions.  Regardless of the languages, you happen to use. Thinking abstract is what divides you from the rest.

Point number two: 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” in ordinary JavaScript.

The obvious good result is that no arguments as expressions are executed before needed if we apply one of the two solutions. They are simply wrapped in a string acting like simple “string lambdas” or in javascript anonymous functions.

Does this post have a limited scope of usability? It sure does. Will I use it? I sure will.

Enjoy … or dislike.

 

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.