Functional JavaScript – Tail Call Optimization and Trampolines

Introduction

The JavaScript Memoization series introduced a recursive Fibonacci sequence generator. Memoization, a method of caching results, was used to enhance performance. Performance can also be enhanced by tail call optimization.

Tail Call Optimization

Tail call optimization is a compiler feature that replaces recursive function invocations with a loop. Eliminating function invocations eliminates both the stack size and the time needed to setup the function stack frames. Both time and space are saved.

The developer must write methods in a manner facilitating tail call optimization. This means the last function invoked must be the invocation of the recursive function. Let’s take a look at the traditional recursive implementation of the factorial function to see what this means.

function factorial(n) {
    if (n == 0) {
        return 1;
    } else {
        return n * factorial(n-1);
    }
}

The factorial function invokes * last, not factorial. This implementation cannot be optimized.

function factorial(n) {
    function recur(n, acc) {
        if (n == 0) {
            return acc;
        } else {
            return recur(n-1, n*acc);
        }
    }
    return recur(n, 1);
}

This factorial implementation features an inner function, recur, providing a delegate for factorial. The recur function is implemented for tail call optimization. The last function invoked is recur. The compiler is thus able to produce code invoking recur in a loop until a result other than recur is returned.

An extra parameter has been added to recuracc. This is the accumulator, the function value accumulated to this point. Let’s trace through the recur method to see how the factorial result is accumulated:

factorial(5) → recur(5,1)
recur(5,1) → recur(4,5)
recur(4,5) → recur(3,20)
recur(3,20) → recur(2,60)
recur(2,60) → recur(1,120)
recur(1,120) → recur(0,120)
recur(0,120) → 120

All recursive functions must have a base case terminating the recursion. The base case for the factorial function is when n=0 in which case 1 is returned. Likewise the base case of recur is also when n=0, but instead of returning 1 the accumulated value is returned.

A Tail Call Optimization Strategy Emerges

Use this strategy for creating optimized recursive functions:

  • Create an inner recursive function having an additional accumulator parameter
  • The base case of the inner recursive function is to return the accumulator
  • The recursive invocation provides an updated value for the accumulator
  • Outer function delegates to the inner recursive function using appropriate initial values

Follow this strategy and your recursive functions can be optimized—providing significant performance improvements. That is as long as your system provides tail call optimizations. Which JavaScript doesn’t.

Bouncing on Trampolines

How can we optimize recursive JavaScript functions without tail call optimization? One option is to re-write your recursive function in loop form.

function factorial(n) {
    var acc = 1;
    for (var i=n; i > 0; i--) {
        acc *= i;
    }
    return acc;
}

Thus implementing the tail call optimization by hand. The downside being the loss of the elegance and expressiveness of the recursive implementation.

Another option is to use a trampoline.

Wikipedia defines a trampoline as “a loop that iteratively invokes thunk-returning functions (continuation-passing style). A single trampoline is sufficient to express all control transfers of a program; a program so expressed is trampolined, or in trampolined style; converting a program to trampolined style is trampolining. Trampolined functions can be used to implement tail-recursive function calls in stack-oriented programming languages.”

That’s a mouthful! Trampolines are actually very easy to understand.

A Simple Trampoline

Here’s a simple implementation of a trampoline:

function trampoline(f) {
    while (f && f instanceof Function) {
        f = f.apply(f.context, f.args);
    }
    return f;
}

The trampoline takes a function and repeatedly executes the return value until a result other than a function is returned. One is tempted to use the trampoline like so:

function factorial(n) {
    function recur(n, acc) {
        if (n == 0) {
            return acc;
        } else {
            return recur(n-1, n*acc);
        }
    }
    return trampoline(recur(n, 1));
}

But this doesn’t produce the desired optimization. Why? Suppose factorial is invoked with n=5. What value is passed to the trampoline? It’s not what you expect.

recur(5, 1)

Is a function expression returning the value 120. The trampoline is invoked with the value 120—not at all what we intended! This is essentially the same tail call optimized implementation of factorial, except tail call optimization is unavailable in the JavaScript environment. What went wrong?

We intended to invoke trampoline with a function reference, not a function result. While the following would work

return trampoline(Function() { recur(n, 1); });

it’s unwieldy. We need a means for obtaining a reference to a function invocation, complete with all the parameters, so we can invoke the function at a later point in time. Though it wasn’t intended for this purpose the Function.bind function fits the bill nicely and thus improve our Factorial implementation:

function factorial(n) {
    function recur(n, acc) {
        if (n == 0) {
            return acc;
        } else {
            return recur(n-1, n*acc);
        }
    }
    return trampoline(recur.bind(null, n, 1));
}

Now we call trampoline with the expression

recur.bind(null, n, 1))

The bind function is a function that when invoked returns the value of the calling function, in this case recur, using the specified calling context (the this pointer, which in this case is null since we’re not calling our function within an object instance), and the list of parameters. The result of recur(n, 1) is returned when this bind function is invoked. This is exactly what we intended to pass to the trampoline—a function!

This implementation is still not yet optimized, however. To see this suppose we invoke factorial with the value n=5. When the trampoline invokes the bind function returning the invocation of recur(5, 1) what result is returned? It’s not a function—instead it’s the result of recur(4, 5) which is 120. We only got one bounce.

This is easily fixed:

function factorial(n) {
    function recur(n, acc) {
        if (n == 0) {
            return acc;
        } else {
            return recur.bind(null, n-1, n*acc);
        }
    }
    return trampoline(recur.bind(null, n, 1));
}

Now the trampoline will provide the desired effect and will continue looping until recur returns the accumulated value.

Mutual Recursion

Can mutually recursive functions be optimized? Consider the mutually recursive functions even/odd:

function even(n) {
    if (n == 0) {
        return true;
    } else {
        return odd(n-1);
    }
}

function odd(n) {
    if (n == 0) {
        return false;
    } else {
        return even(n-1);
    }
} 

Here’s a quick look at the execution of even:
even(5) → odd(4)
odd(4) → even(3)
even(3) → odd(2)
odd(2) → even(1)
even(1) → odd(0)
odd(0) → false

And another:
even(6) → odd(5)
odd(5) → even(4)
even(4) → odd(3)
odd(3) → even(2)
even(2) → odd(1)
odd(1) → even(0)
even(0) → true

One is tempted to write the following to benefit from the trampoline:

function even(n) {
    if (n == 0) {
        return true;
    } else {
        return trampoline(odd.bind(null, n-1));
    }
}

function odd(n) {
    if (n == 0) {
        return false;
    } else {
        return trampoline(even.bind(null, n-1));
    }
} 

Remember the intent of the trampoline was to replace recursive function invocations with a loop. This implementation does not achieve this. Instead the trampoline is recursively invoked. Use the following implementation instead:

function _even(n) {
    if (n == 0) {
        return true;
    } else {
        return _odd.bind(null, n-1);
    }
}

function _odd(n) {
    if (n == 0) {
        return false;
    } else {
        return _even.bind(null, n-1);
    }
}

function even(n) {
    return trampoline(_even.bind(null, n));
}

function odd(n) {
    return trampoline(_odd.bind(null, n));
}

Now the implementation is appropriately optimized.

Performance

The simplicity of the implementation of even/odd allow us to directly measure the performance of the trampoline and compare that performance to the recursive invocation.

Tramplone-vs-Recursive

This graph clearly shows trampolines are not an optimization—tail call optimization is still badly needed in the JavaScript environment. What this graph doesn’t show is that after 30,000 recursive invocations the browser hung; to the point it had to be forcibly shut down. Meanwhile the trampoline continued bouncing through hundreds of thousands of invocations. There are no practical limits to the number of bounces a trampoline can make.

Why does a trampoline not improve performance? A couple of observations should be made about this trampoline implementation:

  • The number of function invocations is not reduced. The trampoline implementation has essentially the same number of function invocations as the traditional recursive implementation. A tail call optimization would eliminate these function invocations.
  • We’ve traded the work of creating stack frames with creating function bindings. Both operations consume time and memory. Tail call optimization doesn’t create function references due to its modifying the function implementation in place to transform the recursive implementation to a loop.

Conclusion

What can we conclude from this? Be skeptical of claims that trampolines improve performance. Demand proof.

Trampolines are still useful even without performance improvement. Your application will run correctly in all operating environments and you’ll encounter fewer hard-to-diagnose errors. Your code is also written to take advantage of tail call optimization when (if?) it’s provided by the JavaScript environment.

Happy hacking!

Posted in Development, JavaScript | Leave a comment

What Hackers Never Learned About the Presidential Candidates

After all the primaries and all the debates we find that several questions that have been on our minds have never been answered (in no particular order):

  1. Are you a Mac or PC? It’s silly, but fanboys everywhere want to know.
  2. Do you support the Open Government Initiative?
  3. Do you believe patents should apply to software? If not, how would you propose to unwind existing patents?
  4. Should software developers require licensing for creating critical software such as:
    • flight control systems/avionics
    • medical monitors/scanners/devices
    • automotive control systems
    • anywhere humans can be killed/injured by malfunctioning software
  5. Do you believe we’re free to rip content that we’ve purchased, audio CDs and DVDs?
  6. Do you believe we’re free to sell our digital purchases? Can we bequeath them when we die?
  7. Should we provide an educational grant to an American student studying in a technical field for every H1B visa issued?
  8. Should the U.S. invest in broadband infrastructure to ensure all citizens have access to high-speed, low-cost data access?
  9. Are open source software contributors indemnified from tort lawsuits?
  10. What are your thoughts on cyber warfare? Is it just to use bombs, guns and missiles in response to a cyber attack?
  11. Do you believe the Federal Government is free to eavesdrop on any OTA transmission, for example cellular calls and data transfers, and any information provided to a public network, for example Facebook, without the need to obtain a warrant? In other words what are your views on privacy in the digital age especially as it relates to federal investigations?
  12. Some groups have advocated that Internet access is a fundamental human right. Do you therefore believe that the suspension of Internet services as part of a sentence for a crime committed is in violation of the 8th amendment of the U.S. constitution?
  13. Do you believe the public’s right to Internet access may be suspended or restricted during times of war or civil unrest?

What questions would you have liked to have had answered prior to going into the voting booth next Tuesday? Share your thoughts in the comments.

Posted in Uncategorized | Leave a comment

Functional JavaScript – Memoization, Part II

Introduction

In Part 1 of this series we looked at a simple function generator for memoizing functions. Recall memoized functions are functions that cache their results; once the value is determined for a particular input the value is placed into a cache and subsequent calls to the function using the same input return the previously cached value — thus eliding the function call. Using a fibonacci sequence generator we examined how memoized functions can greatly improve program performance.

Memoizing Recursive Functions

Let’s take another look at the Fibonacci function presented in the previous post:

// retrieve nth fibonacci number from sequence (0-relative)
// recursive solution for fibonacci calculation is not efficient
function fibonacci(idx) {
   if (idx == 0) return 0;
   if (idx == 1) return 1;
   return fibonacci(idx-2) + fibonacci(idx-1);
}

What’s the problem here? Let’s suppose we’ve generated a memoized version of this fibonacci function, called mfibonacci, and we call mfibonacci(10). What’s happening? At a high level we’re going to return

fibonacci(8) + fibonacci(9)

Do you see the problem? We’re not calling memoized versions of our fibonacci function in our recursive generation! In calculating mfibonacci(10) we’ve calculated fibonacci(9), fibonacci(8), fibonacci(7), fibonacci(6), fibonacci(5), fibonacci(4), fibonacci(3), fibonacci(2) and none of these values have been cached. If after calling mfibonacci(10) we were to subsequently call mfibonacci(9), we’d have to start all over again. The fact we’ve already calculated fibonacci(9) while computing mfibonacci(10) has been lost.

This particular implementation of fibonacci compounds the problem:

fibonacci(n) = fibonacci(n-2) + fibonacci(n-1)

When calling mfibonacci(10) both fibonacci(8) and fibonacci(9) are called. We’d like the result of fibonacci(8) to be cached and available when calling fibonacci(9). This implementation of fibonacci was deliberately chosen for this pathological case.

How do we memoize recursive functions?

Re-Thinking Memoize

We need to backtrack a little bit. Previously we used the following function generator for memoizing functions:

// Return a memoizing version of a function f                                                                
function memoize(f) {
   if (f instanceof Function) {
      // only memoize functions of arity 1, otherwise return function                                 
      if (f.length == 0 || f.length > 1) return f;

      var fn = function(x) {
         if (fn.memoizer.values[x] == null) {
            fn.memoizer.values[x] = f.call(f,x);
         }
         return fn.memoizer.values[x];
      };

      fn.memoizer = { values : [] };
      return fn;
   } else {
      return f; // garbage in, garbage out I always say!                                               
   }
}

While this function generator works, it’s less than ideal. To see what’s wrong consider the following contrived scenario:

   memoize(fibonacci)(10);
   memoize(fibonacci)(10);

Here the function generator memoize is creating a new and separate memoized version of fibonacci — each having their own cache! That’s not what we want. Instead we’d like the function’s memoization to be a property of the function itself. This way everyone getting the memoized form of a function are getting the same implementation, and more importantly, the same cached set of values.

It’s extremely useful to make a function’s memoized form a property of the function itself. In JavaScript functions are first-order objects: they can be assigned to variables, stored in properties, passed as function arguments and so forth. There are many ways you can get a reference to a function and it would be convenient to get the memoized form of the function in a uniform way.

How do we do that?

Function Prototype

The solution is to place the memoizer function and the property to the memoized form of the function in the function prototype. Recall that in JavaScript whatever functions and properties exist in the object’s prototype also exist in all instances of the object. You may also recall that in JavaScript all functions are objects — all being instances of the base Function object. Whatever methods and properties we add to Function.prototype will therefore exist for all functions.

We don’t typically think of functions as being objects having methods and properties, but in JavaScript that’s exactly what they are. Now let’s look at how we can add a memoizer and memoized form property to a Function.prototype:

// Return a memoizing version of function                                                                
Function.prototype.memoize = function() {
    // only memoize functions of arity 1, otherwise return function                                      
    if (this.length == 0 || this.length > 1) return this;

    var _this = this,
    _caller = this.caller;

    if (this.memoizer == null) {
        this.memoizer = {
            fn : function(x) {
                if (_this.memoizer.values[x] == null) {
                    _this.memoizer.values[x] = _this.call(_caller, x);
                }
                return _this.memoizer.values[x];
            },

            values: []
        }
    }

    return this.memoizer.fn.wrap(this);
}

As before we only memoize functions whose arity is 1. this refers to the function object being memoized. We’re adding a new memoizer property to the function. The memoizer property is an object having 2 properties:

  1. fn  The memoized form of this function
  2. values  The value cache for the memoized form of this function

The key difference now is memoize is a property of the function instance. Meaning regardless of how many times we call memoize we’re retrieving the same memoized form for the function.

Once we’ve gotten over the fact that functions are objects and may therefore have their own properties and methods, we need to be careful to delegate those properties in the memoized form of the function. That’s what wrap does:

// Wrap an object, pulling wrapped object properties up to wrapper                                       
// so as to make them accessible                                                                         
Object.prototype.wrap = function(o) {
    for (var p in o) {
        if (o.hasOwnProperty(p)) {
            this[p] = o[p];
        }
    }
    return this;
}

We won’t discuss the wrap function any further at this time. We’ll see the importance of wrap in future posts.

Memoizing Fibonacci

Let’s put this all together and generate a fully memoized form of fibonacci:

// retrieve nth fibonacci number from sequence (0-relative)                                              
// memoized solution for fibonacci calculation is very efficient                                         
function fibonacci(idx) {

    if (fibonacci.memoizer == null || !fibonacci.memoizer._fibonacci) {
        var innerFibonacci = function(idx) {
            if (idx == 0) return 0;
            if (idx == 1) return 1;
            return innerFibonacci(idx-2) + innerFibonacci(idx-1);
        }.memoize();
        fibonacci.memoizer = innerFibonacci.memoizer;
        fibonacci.memoizer._fibonacci = true;
    }

    return fibonacci.memoizer.fn(idx);
}

The innerFibonacci is key to the implementation. It’s a function object assigned to a memoized form of an anonymous function implementation of the fibonacci function. The innerFibonacci references within the anonymous function definition refer to this outer innerFibonacci variable being defined. Thus the recursive form is fully memoized.

The second key to this implementation is aliasing the innerFibonacci memoizer with the fibonacci memoizer. It’s our Function.prototype version of memoize that allows us to do this: it created the memoizer property on the function that we can now refer to. When we retrieve the Fibonacci memoizer we’ll actually be retrieving the innerFibonacci memoizer — which is exactly what we want.

Finally, notice how we nonchalantly call the memoize method on the anonymous fibonacci function we’ve created at runtime. Now you should begin to understand that in JavaScript functions are objects and as such may have properties and methods and that these properties and methods not only apply to named functions but anonymous functions as well.

What’s Next

We’ve seen how to create fully memoized forms of recursive functions. In so doing we’ve created a single value cache for a function’s memoized form. Both are vast improvements to where we were at the end of the previous post when we first considered memoization.

We’re still not done with memoization however. We’ve not looked at any aspect of cache management. Currently our memoization cache will grow without bounds. We may need to limit both the size constraints and time constraints of the cache. We may only wish to cache so many values based off frequency of use. We may be dealing with a function that returns different values over time even when provided with the same inputs, meaning we may need to empty the cache when some time limit has been exceeded.

In future posts we’ll look at ways of enhancing our memoize implementation to provide these capabilities. See you then!

Posted in JavaScript | Tagged | 1 Comment

Maintenance Driven Development

Dodgy Coder wrote a post about old school developers achieving a lot with a little. Developers such as Ken Thompson, Joe Armstrong, and Jamie Zawinski essentially eschew modern development practices and ideologies — Agile, Scrum, XP, TDD — and instead rely on thinking and print statements.

Does this really surprise anyone?

Mark Twain famously quipped he never let schooling interfere with his education. Likewise I don’t allow development methodologies to interfere with my programming.

I tend to build software both top-down and bottom-up simultaneously. I code to the middle. I first start top-down so I can establish the landscape, determine the main actors, where the core functionality lies and establish a rough sense of the required underlying data model, software services and interfaces required to support this functionality. Then I switch to the bottom-up mode to work on the data model and those software services and interfaces to ensure it’s reasonable and will perform as expected. Sometimes changes are needed and so I need to rinse and repeat until I’m fairly satisfied I have a workable solution.

Now I begin the activity most non-developers think of as development. I begin fleshing everything out and connecting it all together. I work on the hard, read risky, items first. That accomplishes two things:

  1. Assurance my solution is technically correct and I don’t need to go back to the drawing board.
  2. Validation of project schedule. If I need to go back to the drawing board then the earlier I do so the better.

So now that I’m coding how do I do it? Mainly by using print statements. Now that I’m mainly doing Java development I use log4j debug statements, but it’s the same thing. Log4j simply gives me the convenience of turning off my print statements in production without having to modify my code.

Of course I test my software, and since regression testing is essential for ongoing maintenance I capture these tests as unit tests. I have a two-fold strategy for unit testing:

  1. Coarse-grained functionality testing
  2. Fine-grained testing of risky implementation items

I’ve come to find this strategy both minimizes the amount of test cases I need to write and also minimizes the risk of bugs slipping into production.

So how is this maintenance driven development?

When I find bugs using my coarse-grained functionality tests I capture it using a newly-created fine-grained test. This way I can repeat the bug, isolate it, and work to resolve it. I proceed in this manner until all coarse-grained tests have passed.

Likewise when bugs are found in production the first step to resolving it is to create a new fine-grained test that repeats and isolates the bug. Then work to resolve it.

The bottom line is I don’t create a lot of fine-grained tests until I need them. When I need them it’s going to be for code that has been problematic at some point in the past, and now that I have a test for it I can ensure it won’t be problematic in the future.

Posted in Development | Tagged | Leave a comment

Functional JavaScript – Memoization, Part I

Introduction

I recently attended Neal Ford’s functional thinking presentation at COJUG. Neal aims to introduce functional programming concepts to traditional developers. Neal covered a lot of ground but for now I want to focus on one aspect of his presentation: memoization.

Memoization

A key concept of functional programming is a function’s result is solely determined by its inputs. A function called with the same inputs produces the same result every time. The result is not determined by some transient state not captured by the inputs.

Since the same inputs are always mapped to the same result then we can cache the result in a key/value map and return the result from the cache, if present, instead of calling the function. In the case of a cache miss we would call the function with the inputs and insert the result into the cache. Such function result caching is called memoization. Memoization is a useful technique for functions that are called repeatedly with the same set of inputs but whose result is relatively expensive to produce.

Higher-Order Functions

Functions are first-class objects in functional languages. This means functions can be assigned to variables, passed as inputs to other functions and so on. The ability to provide a function as an input to another function allows us to create higher-order functions. When we say ‘provide a function as an input’ we’re talking the function itself, not the result of the function applied to some set of inputs. You simply can’t do such things in non-functional languages.

We can do some pretty amazing things by combining the ability to provide a function implementation as the input to another function with the ability to generate functions at runtime as needed, via function generators. One of those things is creating a higher-order memoization function generator. We can create a memoized version of a function as needed at runtime and then use the memoized version wherever we would have used the non-memoized version. They’re interchangeable.

Function Generator for Creating Memoized Functions

Creating a memoization function generator in JavaScript is simple:

// Return a memoizing version of a function f                                                                
function memoize(f) {
   if (f instanceof Function) {
      // only memoize functions of arity 1, otherwise return function                                 
      if (f.length == 0 || f.length > 1) return f;

      var fn = function(x) {
         if (fn.memoizer.values[x] == null) {
            fn.memoizer.values[x] = f.call(f,x);
         }
         return fn.memoizer.values[x];
      };

      fn.memoizer = { values : [] };
      return fn;
   } else {
      return f; // garbage in, garbage out I always say!                                               
   }
}

Analysis

Lines 7-12 is the heart of the implementation. This is the function being generated and returned. This generated function contains a memoizer property containing the cached function results. When this generated function is called it first checks its cache to retrieve the value. In the case of a cache miss it simply calls through to the function f supplied to the function generator and caches the result. In both cases the cached result is returned.

The rest of the implementation is housekeeping. In line 3 we check to ensure f is a function and if not we return f, whatever it was, in line 17. Line 5 checks the number of inputs, or arity, of f. To simplify things we only memoize functions whose arity is 1 (unary functions). There’s an elegant solution for memoizing functions of higher arity that I’ll be demonstrating in a future post.

f is returned in the case where f isn’t a unary function. This is fine since memoize(f) and f are interchangeable functions producing indistinguishable results. The only downside is the caller may believe it’s using a memoized version of the function when it isn’t. The caller can determine whether the function it’s calling is a memoized function by checking for the existence of the function’s memoizer property.

Fibonacci

To demonstrate the powers of memoization we need a function to memoize. This example utilizes a fully-recursive fibonacci sequence generator. The parameter is the 0th—based index of the fibonacci sequence whose corresponding value is to be returned. This is an extremely inefficient implementation thus illustrating the potential computational time savings of memoization.

// retrieve nth fibonacci number from sequence (0-relative)
// recursive solution for fibonacci calculation is not efficient
function fibonacci(idx) {
   if (idx == 0) return 0;
   if (idx == 1) return 1;
   return fibonacci(idx-2) + fibonacci(idx-1);
}

Memoizing Fibonacci

Creating a memoized version of this fibonacci function is simple:

var fn=memoize(fibonacci);

The function fn is the memoized version of the fibonacci function. fn may be invoked in lieu of the fibonacci function like so:

fn(35);

yielding the result 9227465 in 273ms of execution time. Once we’ve retrieved the value once for 35, subsequent invocations for 35 yield the result in 1ms, according to the Google Chrome profiler; which has a minimum resolution of 1ms—so the actual time may be less!

What About Memory Utilization?

Memoization illustrates the classic execution time vs. execution space trade-off. More memory is consumed by caching the results but there’s the potential to save a lot of execution time. If you need to clear the function result cache to free up some memory then simply do the following:

fn.memoizer.values.length=0;

and the cached values will be released.

Insufficient Memoization

The recursive definition of fibonacci may have caused you to wonder whether the intermediate results are cached. They are not. So when computing

fn(35);

we’ve also computed:

fn(34), fn(33), fn(32)…fn(2), fn(1), fn(0)

though none of these interim results are cached. In future posts we’ll consider ways to cache these interim results.

What’s Next

In future posts I’m going to further expand upon the ideas presented here addressing both the memoization of the interim results of recursive functions and memoizing non-unary functions. We’ll even be looking at ways to make the memoization generator function a property of all functions. See you then!

Posted in JavaScript | Tagged | Leave a comment

DRY Career Advice

You’re probably familiar with Dave Thomas and Alan Hunt’s Don’t Repeat Yourself (DRY) principle first discussed in their book The Pragmatic Programmer. Broadly speaking the DRY principle can be interpreted as don’t implement the same functionality twice within a given system. Or doing the same thing two different ways within a given process.

We’re accustomed to applying the DRY principle to our designs and implementations, but what about our careers?

To paraphrase an infamous interview question what do you see yourself doing in 5, 10, 15, or 20 years? I know you still want to be programming at some level. That’s why you’re reading this blog: you’re passionate about programming and creating software. You love it. You seek to continually improve your craft. You’re going to continue programming until you retire. Even then you won’t stop and will continue programming until you expire!

Many of us are in this passionate programmer category. But we’ve also dodged the question. Given our nature and passion for programming it’s easy to say we’re going to be programming 20 years from now. It’s much more difficult to say what we’re going to be programming.

I’m not talking languages and tools – the things you’ll be programming with. I’m not talking about computers, clouds, processors and memory – the things you’ll be programming on or for. We can be sure these things will change, in some ways we can’t imagine, over the course of the next 20 years.

I’m talking about what kinds of systems will you be creating? The same kinds you’re creating today? Really? Twenty years of creating the same kind of software? That sounds pretty boring. It’s also potentially career limiting. Not to mention how do you really improve your craft when you’re recreating the same thing over and over again?

Working in industry verticals or niche markets is OK. But you don’t want to find yourself creating the umpteenth accounting system 20 years from now and trying to console yourself that it’s somehow different because the implementation language is different. Or the IDE you’re using is different. Or the type of machine it runs on is different. It’s not different. It’s the umpteenth accounting system you’ve created.

If you really want to spend an entire career creating software then I have one simple advice: Don’t Repeat Yourself. Don’t create the same system twice.

Posted in career | Tagged , | 1 Comment

The PC is Dead! Long Live the PC!

I bought a 3rd generation iPad the day they were first available. I didn’t camp in line or anything of that sort. I simply went to Best Buy after work, walked up to the counter and selected the model (black, 32 Gb, WiFi) I’d intended to buy. There were 6 remaining. By the time I’d left the store there was 1 remaining, so sales were pretty brisk.

I used the upcoming spring break family vacation to rationalize making the purchase on the first day the new iPad was available (I’d intended to buy one anyway). I also grabbed the Camera Connection Kit and a Smart Cover.

Setup was a breeze since I already have an iPhone. Just spend a few minutes syncing with iTunes and I was good to go. I was pleasantly surprised to find my iPad had 100% charge on its battery. I’d expected to have to charge it first.

I let the kids pick out a few games since I knew there would be quite a bit of driving for this vacation. I was hoping the new iPad would keep them entertained during the drive. Worked like a champ! Not only did it keep the kids entertained, with a minimal amount of fighting over who gets the iPad next, the battery life exceeded my expectations. Seven hours of continuous use playing games and the battery had only been drained down to 20-25%!

Every hotel these days has a free WiFi connection so I never missed not paying the extra $150 for the 4G LTE connection for the iPad. It was nice at night to chill out and everyone tell their friends what they did that day on Facebook. I was responsible for uploading, editing and organizing the day’s photos – that’s why I grabbed the Camera Connection Kit – posting the ‘Photo of the Day’ on Facebook. It was in doing this task that I discovered the iPad will charge your camera. This seems like a pretty useful feature, but I couldn’t find a way to control it. It appears the iPad is going to start charging your camera whether you want it to or not. I could imagine in low battery situations where you may not want it to.

You’ve all heard the new retina display is gorgeous and it’s true. Text looks printed, not displayed. Let me put it this way – it’s easier to read than my Nook which uses E Ink and is a dedicated e-reader. Pictures are fabulous and really made my Nikon CoolPix S8200 shine. You should consider the new iPad for digital picture viewing alone. Nothing compares.

Since I didn’t have another computer with me for the week I got adept at touch typing on the iPad. I still have problems with my left pinky hitting either ‘q’, ‘w’ or even sometimes ‘a’ (ha!) So it isn’t without issue. It’s the auto-correct feature that makes it work so well. While the single-key error rate is a terrible-sounding 20%, especially where the left pinky is conerned, the iPad is able to determine what word you meant to type and automatically insert that word instead. The auto-correct feature is accurate greater than 95% of the time so overall touch typing is quite pleasant on the iPad. And it certainly beats using your thumbs.

To make typing perfect the next iPad needs a haptic keyboard. That way your fingertips can rest on the keys, feel the ridge on the home keys, and detect the push that your depressing that key. You could do some serious typing with such a keyboard. In my opinion that would be worth the purchase price of a new iPad just to get that one new feature!

So far I haven’t told you anything you didn’t already know – the iPad is a consumption device with a gorgeous display that now coupled with the Camera Connection Kit allows you to import/edit/organize your photos. But what else can you do with it? Would you believe programming?

All you need is the Gambit Scheme application, available in the App Store for 99¢! I was reading Paul Graham’s On Lisp on the iPad – remember the iPad is a great e-reader? – when I got to the chapter on continuations. Paul first explores how continuations are used in Scheme before considering how to implement them in Lisp. Using Gambit I was able to follow along just fine creating and running sample applications on my iPad. The Gambit REPL though makes you acutely aware of the keyboard problem. There’s no auto-correct for Scheme source so that 20% single-key error rate starts to grate on your nerves. Otherwise you’re able to create and run Scheme programs on your iPad! (And yes, this is what geeks do when chilling out on vacation – program! We just like to program something different from what we’re doing at work at the time).

Adding to the iPad’s producing cred is the fact Apple has brought out their iLife and iWork application suites for the iPad, priced at $4.99 per app. Adobe has already released Photoshop Touch for the iPad priced at $9.99. Even Microsoft is rumored to be bringing out their Office suite on the iPad later this year, though no pricing is available.

Clearly this all points to the death of the PC, especially for typical home/family usage. So why long live the PC? A colleague recently pointed out you wouldn’t want to do your taxes on an iPad. He’s right – the keyboard would drive you nuts. Likewise a typical knowledge worker in an office environment is not going to want to use an iPad either. Even if it had an awesome haptic keyboard. It’s simply not the right tool for the job. So there’s room for the PC for the foreseeable future, especially in the workplace. But for everywhere else? Watch out! The PC may be going the way of the dodo bird.

Posted in iPad | Tagged | Leave a comment