Idea: Flow Control Functions
Wednesday, May 13th, 2009Background: Flow Control Syntax
Your average conventional programming language can be logically divided into two parts, (1) the metadata part and (2) the instruction part. These are usually mixed together in one unifying syntax, but it is easy to logically separate the two in your mind. The instruction part is the body of the functions. The metadata is everything else (or, well, usually. C#, for example, lets you put expressions into initializers which is technically instructional syntax, albeit crippled syntactically – for the purposes of this post, this is irrelevant so we’ll ignore it). We don’t care about the metadata for this post, just the instructional code.
You can then further logically divide the instructional syntax into the (1) flow control and (2) statements. The statements are the expressions (including math like “a + b * 3” and function calls like “foo()“) and assignments (such as “variable = 0“). This leaves the flow control statements, for, foreach, do, while, if/else, try/catch, etc… Not every language names them identically, and not every language has every construct, but every language has flow control syntax. Flow control is what this post is about.
Traditionally, these statements are built into the language at the language level and are available even if the standard libraries are unavailable. There are some exceptions, such as in more functional languages like Lisp, and even Ruby.
This is precisely the basis for this post. I want to explore what it would take to build all of these into the standard library, while simultaneously making it possible for developers to create their own. What are the pros, and what are the cons?
“Functionizing” Control Flow
Ruby allows something like this. What you might do with a for loop in one of the traditional languages, in Ruby, you do with a function that takes another function as a callback. So instead of:
for (i = 0; i < 4; i++) { do(stuff); }
in ruby, you use the “times” method of integers:
4.times { do stuff }
There is a price to be paid in performance for this ability, in Ruby. That block of code is an anonymous method in Ruby and requires a managed object which has to be allocated on the heap, then later collected for every usage. The for loop, above is all inline. The block of code that makes up the body of the loop is not used anywhere else, so there’s no point in setting up an object on the heap etc. This makes the for loop above much faster than the Ruby alternative.
A smart enough compiler, on the other hand, could give you the convenience of the function-based control flow with the native speed of the implementations in the conventional procedural languages.
Additionally, I think that with a slick enough language, it should be possible to implement most, if not all, of the control flow models listed before. (i.e. not just for, and while, but also if/else, and try/catch). This way, things like while would be implemented as special “control flow” methods included in the standard library. This detail would be unnoticeable to most developers at first, but it opens up a whole new world in terms of language extensibility.
The while implementation is straightforward (what the syntax should look like, I am not yet sure), but basically it defines a way to test a condition which would act like an input parameter, and then based on that condition, run a block of code, after which it would test the condition again and so on. It’s simple. A foreach would work similarly in that it would take a block of code, but instead of a condition would take an enumerable object. It may also, for example, initialize a variable before every loop to represent the current object.
Those are straightforward ideas, but the best part about this is that it should allow you to invent your own control flows, such as a switch-like statement which worked by hashtable, or something like a custom synchronization lock which allows you to do something within a block of code after which the lock is released (like C#’s “lock” keyword, but customizable). You could also create a with-like statement that I’ve described in my page about reinventing the development environment.
There are probably other uses for this I haven’t yet thought of. These are the pros. There are potential cons, however. The one I worry about the most is that, as a general rule, if you design a tool for good, bad developers will find a way to use it for evil in ways it was never designed. (Sometimes they seem to do these things for no other reason than to accomplish tasks in the most obscure way possible). Extra precaution should be taken to curb that where possible, and also make it intuitive for developers to still be able to read, and navigate someone else’s code.