On syntax

Forget about what programming languages you know. What syntax do you wish you could write in? (Names have been changed to protect the innocent):

This?

    function generate_fibonacci_sequence( $length ) {
        for( $l = array(1,1), $i = 2, $x = 0; $i < $length; $i++ )
                $l[] = $l[$x++] + $l[$x];

        return $l;
    }

What about this?

    sub fibo
    {
        my ($n, $a, $b) = (shift, 0, 1);
        ($a, $b) = ($b, $a + $b) while $n-- > 0;
        $a;
    }

Getting better?

    def fib(n):
        if   n < 2: return 1
        else      : return fib(n - 1) + fib(n - 2)

Hmm, isn’t this going backwards?

    (define fibo
     (lambda (x)
       (if (< x 2)
         x
         (+ (fibo (- x 1)) (fibo (- x 2))))))

Finally:

    fib 0 = 0
    fib 1 = 1
    fib n = fib (n-1) + fib (n-2)

If the programming languages of the future don’t look like the last example, I think we’ve failed. Programming languages are notation for describing solutions to problems. If that notation is verbose, clunky or full of special cases it should be abandoned in favour of a more concise notation. Real languages should look like pseudcode!

My suspicion is that in 50 years time we’ll look back on the syntax of today’s programming languages and think them highly bizarre, like looking back at Frege’s logic notation (pdf):

Syntax has come along way since the 1960s, but there’s still room for much improvement, looking at the widely used languages employed today. The designers of many (not all) popular languages seem to forget that syntax should be optimised for writing by humans, not for parsing by machines.

Type inference made mechanical

Haskell uses type inference, so explicit type declarations are rarely required. This:

    import Control.Monad.Fix
    fibs = fix ((1:) . scanl (+) 1)
    main = print (take 20 fibs)

Is just as good as:

    import Control.Monad.Fix

    fibs :: [Integer]
    fibs = fix ((1:) . scanl (+) 1)

    main :: IO ()
    main = print (take 20 fibs)

Running this:

    $ runhaskell A.hs
    [1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765]

Now, once programs reach a decent size, the advantage of type declarations appears: it functions as machine-checkable documentation. So people new to your code can more quickly work out what the code is doing.

Contrast this (real world) Haskell code from a network client:

    accessorMS decompose f = withMS $ s writer ->
      let (t,k) = decompose s in f t (writer . k)

And with type annotations:

    accessorMS :: (s -> (t, t -> s))
               -> (t -> (t -> LB ()) -> LB a)
               -> ModuleT s LB a

    accessorMS decompose f = withMS $ s writer ->
      let (t,k) = decompose s in f t (writer . k)

So at least you know have some idea of what that code does.

There’s an intuition here: type declarations are good, but they can be mechanically inferred. Let’s automate that then! Here’s a quick script I use every day. It just passes your top level declaration to ghci, and asks it to infer the type. The resulting type signature is spliced back in to your code:

    #!/bin/sh

    # input is a top level .hs decls

    FILE=$*
    DECL=`cat`
    ID=`echo $DECL | sed 's/^([^ ]*).*/1/'`
    echo ":t $ID" | ghci -v0 -cpp -fglasgow-exts -w $FILE
    echo $DECL

Save this as an executable shell file in your path. Now you can call this from your editor. For example, from vim, you’d use the following .vimrc:

    :map ty :.!typeOf %

Hitting :ty while the cursor is positioned on top of a top level declaration takes your code from:

    writePS who x = withPS who (_ writer -> writer x)

to this:

    writePS :: forall p g. String -> Maybe p -> ModuleT (GlobalPrivate g p) LB ()
    writePS who x = withPS who (_ writer -> writer x)

I can’t emphasise enough how useful this is. So, steal this code and improve your productivity!