Haskell, maths and Threes

· geek · code ·

This is a long and slightly rambling story about my relationship with maths. There is a pattern to it, even if it isn’t always clear how the parts are connected.

While I love almost every other aspect of playing with computers, for some reason I don’t actually play games on them very often. I’ve downloaded the odd game on my iPhone, but I rarely launch them. My last serious game addiction was to Tetris. When I was supposed to be writing my PhD thesis, I took breaks at intervals to play Tetris on my Powerbook 100, with its tiny, monochrome screen. I never got very good at Tetris, but the absorbing mindlessness of it helped to give my brain a break from stringing words together.

I mention this because I seem to have become mildly addicted to a new iOS game called Threes. It’s one of those ‘deceptively simple to play, but maddeningly difficult to master’ kinds of games. You are faced with a board containing tiles with numbers on, and you have to slide these around the board. When you slide two identically-numbered tiles on top of one another (by sliding them against a ‘wall’), they collapse into one tile showing the sum of the two numbers. The only exception to this rule is that you can slide ‘1’ or ‘2’ tiles on top of each other to make 3 (all the other tiles are multiples of 3, hence the name). The aim is to score as high as possible before the board fills up with tiles and you can no longer move.

While I was sliding a 3 over a 3 to make a 6, and then dragging that 6 over an adjacent 6 to make a 12, it suddenly struck me why this process seemed familiar: it was a particular, concrete example of ‘folding’ in Haskell. Haskell has a couple of functions (foldl for ‘fold left’ and foldr for ‘fold right’) which process lists down to a single item by accepting a function and a list and applying the function to each item of the list, then recursively applying the function to the rest of the list1. The result of the function is the accumulated value. I had a little blip of joy at recognising a pattern. Haskell involves a lot of patterns, and a lot of maths.

I don’t exactly know why I decided to try to learn Haskell properly. I had first encountered this purely-functional programming language when I was running Arch Linux on a laptop, and using XMonad (written in Haskell) as a window manager. I learned just enough about the syntax of Haskell to be able to tweak my XMonad configuration, but I was impressed (and slightly baffled) by Haskell’s elegance, and by the strangeness of a functional language, having only experienced imperative programming languages. I’ve written before about the fact that I like learning different programming languages, so I decided to dip my toes a bit deeper into learning Haskell.

I am certainly still a beginner, and I can’t say that it has been easy, but it has been really interesting learning Haskell. More — I think — than in other languages, patterns are really important to thinking about how to solve programming problems. Haskell functions (indeed, entire Haskell programmes) are mathematical expressions that are evaluated. This means that you need to think about how you want your input to be transformed, rather than thinking about a sequence of actions to take. Thus, recognising common patterns becomes extremely important in solving new problems, but also in thinking about other ways to approach them. In this respect, it helps if you are good with mathematics, and therefore familiar with the idea of breaking a problem down to reduce it to a simpler expression.

Maths and I have never been friends. It was consistently my least favourite subject at school, and the only subject in which I just gritted my teeth and tried to find a way to pass the exam, rather than wanting to understand it. I found it dry, abstract and largely incomprehensible. I have always liked concrete problems and examples — things I can visualise. My Dad, an engineer, was (and is) excellent at maths, and tried his best to help me when I got stuck with my maths homework. He’d say, “But you see, it’s much easier if you approach it this way…”, and I would whine about wanting to do it the way we’d been taught. I see now that he was trying to show me that if I understood the patterns, I could tackle any of the problems much more easily. At the time, I couldn’t see that at all. I hadn’t yet learned to enjoy spotting patterns, or to find analogies to help me visualise processes.

In my chosen branch of biology, I could get by with a fairly superficial understanding of maths. However, once I started to analyse data, I got interested in statistics, and started to enjoy trying to make sense of my results. Of course, I also started learning to programme, and consequently some mathematical concepts crept so stealthily into my brain that my ‘I hate maths’ defensive reaction wasn’t triggered.

Now a list of experiences (Threes and Haskell and my growing enjoyment of patterns) fold elegantly — through a currently mysterious function — and evaluate to maths.

Last week I had to help some students with a maths exercise they had been set. I had the exercise they had done, and the answers, and was trying to work through it myself so that I could support them. Oh, my poor old brain. It really was hard work. The statistics question was the only one I could do easily. I don’t work in a traditional lab, so it has been more than 20 years since I learned about dilutions, molarity, how to calculate rates, and so on. Nevertheless, I was determined that it wasn’t going to beat me, and with a lot of scrap paper, abundant use of Calca to check my understanding of bits of equations, and assistance from the internet, I got through it. To my surprise, I found myself enjoying the challenge. I was playing around with expressions, trying to find other ways to solve the problems, experimenting and exploring. I’m not sure that maths and I will ever be best friends, but let’s say that we’re beginning to get acquainted.

  1. Granted, the Threes example would have to constrain the fold function by allowing a list item to fold only if it was equal to the value of the accumulator, but you get the idea. ↩︎