Haskell Hacking: a journal of Haskell programming

2006-12-15

More on syntax

Yesterday's article, about programming language syntax in an ideal world, provoked a small storm of controversy over on reddit.

Unfortunately, since there were a couple of other Haskell articles on the same page, the article's argument, that languages of the future should look like pseudocode, was somewhat lost. A number of readers assumed the article was a piece of Haskell advocacy, which wasn't the intention. Instead, it was a plea for the syntax of future languages to be optimised for writing by humans, and not for parsing by compilers. That is:

The languages of the future should look like pseudocode

I'll cache the original example of "future syntax" here, so you can keep it in mind:

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

Note that this is an argument for syntax, independent of whatever language you want to use. And it ignores all efficiency issues in current languages (that's a job for the compiler designers of the future).

Of course, programmers being very practical people took this as an opportunity to demonstrate how their language of choice (and being reddit, this means lisp) already provides this "future syntax". Well, sort of:

    (define fib
      (match-lambda
        [0 0]
        [1 1]
        [n (+ (fib (- n 1)) (fib (- n 2)))]))
    (define (fibo x)
      (cond
        ((= 0 x) 0)
        ((= 1 x) 1)
        (else (+ (fibo (- x 1))
                 (fibo (- x 2))))))
    (define (fibo x)
      (case x ((0 1) 1)
              (else (+ (fibo (- x 1)) (fibo (- x 2))))))
    (defmethod fib ((n (eql 0))) 0)
    (defmethod fib ((n (eql 1))) 1)
    (defmethod fib (n)
          (+ (fib (- n 1)) (fib (- n 2))))
    (define (fibo x)
       (if (< x 2)
           x
         (+ (fibo (- x 1)) (fibo (- x 2)))))
    (let ((fibs (make-hash-table)))
       (defun fib (n)
          (if (< n 2) n
              (or (gethash n fibs)
              (setf (gethash n fibs)
                    (+ (fib (- n 1)) (fib (- n 2))))))))

Others focused on improving the particular languages I cited. The best contribution on this issue came from Greg Buchholz, who illustrated neat implementations in a variety of languages, far more flattering than the examples I used (which, incidentally, were not my creation, they were wikipedia's ....)

Perhaps unsurprisingly (in hindsight) plenty of readers assumed the plea for future syntax was a plea for "Haskell now!", and talked at length about efficiency concerns, and the lack of real world code/IO in the examples:

    all of these are monstrously inefficient...
    > let fibs = 1 : 1 : zipWith (+) (fibs) (tail fibs)
    > take 10 fibs
    [1,1,2,3,5,8,13,21,34,55]

    That's surely more efficient
    And kinda memory eating :-)

    You'd better choose something like

    fib = fib_iter 1 1
      where
        fib_iter x y n | n == 1 = x
                       | otherwise = fib_iter y (x + y) (n - 1)
    there's a much more efficient way to do it in Hs using the language's
    lazyness (the ability to recursively define infinite lists):

    fibs = 1 : 1 : zipWith (+) (fibs) (tail fibs)

And, this being the interblargh, some people missed the point entirely:

    This code doesn't have any resemblance to everyday programming however -
    which generally involves trawling through databases, string matching, and I/O.
    What is the point of showing examples which do not reflect reality of
    most(?) programing tasks?
    I've been a ASM/C++/C# developer for over a year now. But seriously, how
    stupid is this? You are going to tout a language strength due to how
    efficiently it can work out the Fibonacci sequence?
    That is not a real-world example, and if you were to use a real-world
    example, this code would be horrific.

So what can be made of all this? I think that it can be objectively stated that current programming languages, for the most part, don't look like ideal pseudocode languages, with syntax often bogged down in language features, and parser hints. Overuse of keywords (defun,define,def,let rec), extraneous typing hints (like $x), and parser hints (';' and '{' '}'), all obscure the underlying code. Step back for a second: you're 10 years old, and you're hoping to become a cool hacker. What syntax do you wish you could use?

Now Python, Haskell and the Lisp family seem to be the closest to where we want to go, but they're not there yet. Even in Haskell, where years were spent sorting out the syntax, and as a result has some nice features for human programmers (pattern matching on data structures, and lovely whitespace), you can still end up writing awkward code (and this is entirely my own fault):

    withGS :: (g -> (g -> LB ()) -> LB ()) -> ModuleT (GlobalPrivate g p) LB ()
    withGS f = withMS $ \state writer ->
      f (global state) $ \g -> writer $ state { global = g }

    readGS :: ModuleT (GlobalPrivate g p) LB g
    readGS = global `fmap` readMS

    writeGS :: g -> ModuleT (GlobalPrivate g p) LB ()
    writeGS g = withGS (\_ writer -> writer g)

    forkLB :: LB a -> LB ()
    forkLB f = (`liftLB` f) $ \g -> do
                forkIO $ do
                    timeout (15 * 1000 * 1000) g
                    return ()

    liftLB :: (IO a -> IO b) -> LB a -> LB b
    liftLB f = LB . mapReaderT f . runLB

So finally, if you're in any way involved in the design of syntax for a new language, or fixing up the syntax of existing languages, please think hard about what you would have wished to program in, had you never been exposed to the languages of today. Programming languages don't have to be ugly and noisy! Concentrate on what the human author of the code would like to write: code that is aesthetically pleasing and even beautiful, and don't compromise your syntax, for the short term gain of making parsing easier.

/home :: /haskell :: permalink :: rss

About

Real World Haskell

RWH Book Cover

Archives

Recommended

Blog Roll

Syndicate