Contact Info

(for those who care)

Instant Gratification   



Sat, 07 Feb 2009

Run-on Programming Redux

I want to revisit run on programming because there are some subtleties that I don’t think were clear enough in the first go around.

The example again (reproduced at the bottom) is like a run-on sentence but with functions. It is characterized by a series of functions or statements that have neither function parameters nor return values, especially such that functions written in this style call other functions written in this style.

Implicit in this code organization are two items which necessarily impact maintainability and reliability.

These two issues lead necessarily to a third point, that the function or class in question is nigh-upon impossible to test.

The functions depend on or modify global (or class) state

A “good” function depends only on its input arguments. function area( radius ) { return 3.14 × radius × radius; } is a canonical example. A function that takes no input arguments had better always return the same value (making it a constant) or be a getter. If it the function results change from invocation to invication, it necessarily depends on or modifies global/class state.

The functions are order dependent

If a function takes no parameters but returns no value then it uses global/class state, or must necessarily modify global/class state. A canonical example might be function print() { echo x + “, ” + y; } or function reset() { this.x = 0; this.y = 0; }.

That means that class.reset(); class.print(); is different from class.print(); class.reset();.

The functions are nigh-impossible to test

In the silly example below, you can kindof test the functions used in the last block (soTired(), and finish, things, up). But to test functions in imTired() you must either mock soTired() or finish, things and up. Going back to doSomething() the problem is compounded even more. To test any one individual component requires you to new the object from scratch and call the dependent functions in a particular order per each test.

The other problem is that temporal dependence is not clear from the program structure. What happens if you called up(); finish(); things(); instead of finish(); things(); up();? It is not possible to know strictly from the existing code whether changing the order will introduce bugs.

A functional alternative to run-on-programming

With everything I’ve learned about functional programming, I’ve learned it is better to explicitly deliver global state to functions in form of parameters, as well as to generally delegate global state modification to the calling function.

Following those simple guidelines generally leads to independent, testable, and re-usable functions where temporal dependence (calling order) is represented more explicitly by function parameters and return values.

Using the print() and reset() examples, you could imagine that we could write a print() function that took a class to print as input, and instead of printing it, returned a string.

function print( class ) => String
{
    return class.x + ", " + class.y;
}

Now the print() function is side-effect free, testable, and doesn’t depend on global / class state (look up the Law of Demeter for a slightly less extreme example).

What about reset()? It simply becomes a factory method.

// This is bad for different reasons...
// please see the eminently wise and hard to type
// blog of Miško Hevery for more information.
function reset() => MyClass
{
    return new MyClass();
}

This pushes responsibility for modifying state to the calling function (a-la Haskell if I’ve read the literature correctly). In the case of print(); reset(); or reset(); print();, it instead becomes:

function one()
{
    x = new MyClass();
    echo x.print( x );   // print the class before resetting
    x = x.reset();
}
function two()
{
    x = new MyClass();
    x = x.reset();       // reset the class before printing
    echo x.print( x );
}

Each function is testable. Side-effects are contained only in the calling function, functions are order-dependent, but that order is explicit via function parameters and all is right in the world.


This is the exaggerated (anonymized) code snippet that inspired the original run on programming post.

    function doSomething()
    {
        doThis();
        doThat();
        doTheOther();
        imTired();
    }

    function imTired()
    {
        doMore()
        andMore()
        andEvenMore()
        soTired()
    }

    function soTired()
    {
        finish()
        things()
        up()
    }

09:48 CST | category / entries
permanent link | comments?

Like what you just read? Subscribe to a syndicated feed of my weblog, brought to you by the wonders of RSS.



Thanks for Visiting!