A funny thing happened to me over the past two years of playing around with F#.
Programming became fun again.
I’ve always enjoyed programming, but in the OOP world, programming quickly became more about structure and frameworks than about solutions. When I learned C++ in Windows, moving from C, I went from a world in which small things happened in a particular order to a large world in which your code had to fit inside a rigid framework. The Windows message pump was butt-ugly, but at least you could look at it and understand it from top to bottom. MFC did the same thing wrapped up in classes. While simpler, it was also farther removed from reality.
And that’s the way most OOP solutions were constructed – abstracting the problem away from the metal far enough that you were dealing in abstracts. Need to make a new windows form? It’s CForm, or System.Windows.Form.Fom — whatever it is, you just make a new one. It knows all about how to run forms on Windows. OOP is a beautiful thing.
But with abstractions come gaps in knowledge. A lot of the problems I see that Windows programmers have is related to not understanding what’s going on under the hood. Need to do a lot of string building? Just use the “+” operator over and over again. Never mind that each string addition could cause a GC. (There is a StringBuilder class for just this scenario) Need to run a bunch of things in parallel? I’m sure there’s some objects out there for that too.
I like good Object-Oriented Programming. On a good project you design the solution, create the scaffold, then plug in the details. Everything has a place. It’s easy to share the codebase. A defined problem has a defined (and architected) solution. Planned changes lead to built-in flexibility in your model — if you’re smart. OOP methodologies are software engineering.
So it was with some trepidation that I ventured into scripting and functional code. It all seemed so idiotically simple. After all, who couldn’t write a function? Who couldn’t refactor a function? These were the simplest of skills, not worth much discussion after you learn and begin applying best principles of coupling and cohesion.
But I found out some really neat things. When I first wrote my first functional code, I would write these butt-ugly imperative loops and such. I might end up with 80 lines of code.
But then I started refactoring, condensing, generalizing. As I worked my code, hidden similarities revealed themselves. Pieces came together in unexpected ways. Like some kind of weird magic trick, 80 lines of code became 50, then 20, then maybe 15 lines or so.
And it was a thing of beauty. It was impossible not to realize this — being able to express a solution to perhaps a complex problem in just a few lines gives you the feeling of getting closer to knowing some kind of ultimate truth. It’s one thing to understand currying and functional composition and all of that. But it’s another thing entirely to watch these tools come together in these unexpected ways to make these elegant solutions that you didn’t see before.
In OOP, you know the answer before you start. It’s all laid out in your OOAD. In FP, even though you have the same general understanding of the answer before you begin, there’s a “revealing” that occurs as the code tightens up. It gives you the feeling of solving a puzzle, not just slinging code.
That was fun enough, and I went on for several months annoying all my friends with how “clean” F# was to code. It got better, though. Sometime in the last year or so I found myself doing something frequently that I very, very rarely did before.
I found myself re-using code.
Everybody talks about re-using code. It was supposed to be one of the huge benefits of OOP, but it never worked out that way. The problem was that OOP code usually lived in these huge frameworks. They had to inherit from such-and-such a form. They had to use all these libraries. And an object was a monolithic thing. In for a penny, in for a pound. If done correctly, you ended up with these nicely-sized hunks of data and code — which were specifically constructed for that particular problem in that particular environment.
But FP isn’t like that. I remember the time I wanted a function to slice lists. Given a linked-list, slice it into 2 pieces at midpoint x. I could have developed the solution, but I just googled it and found a neat little piece of OCAML code that did it in just a few lines.
On another project I had a requirement to take some data objects and convert them into JSON to send over the wire. The “old” way would have been to write a “ToJson” method on each object, hand-writing in the return string (or using some kind of code generation to automate it). This time, however, I decided to bite the bullet. I wrote a generic app that takes any .NET object and makes a JSON string — even complex objects. As part of that, I ended up with a nice little function that will walk a .NET object, providing a callback. That means you can do all sorts of things, not just write JSON. You could take the same code and use it for binary persistence. Or for runtime reflection. There are all sorts of little uses.
I became a code-snippet collector.
It’s like collecting stamps. I have this neat piece of code to create a irregular-shaped window. One for transparent bitmap buttons. Another one for playing disk sounds. Here are some type extensions to handle the old-style collections. Here’s another one for posting messages from a worker thread back to the GUI.
These are all things that I have done before, and quite frankly, they’re not that hard to implement. Before — if I had the time — I might write up some of this in a class. Say 100 lines of code. But who the heck wants all of that trouble? Most of this stuff is just a few lines of F#. Small enough to be portable and easy-to-use, yet complex enough to warrant keeping around. Any simpler and it wouldn’t be worth keeping. Any more verbose and it’d be a pain to keep and reuse.
I’ve also found that these pieces come together wonderfully in “exploratory” programming. Not traditional business programming, where you know the problem, you’ve defined the problem, you’ve designed the solution, and now you go code. Nope. Exploratory programming is much more like painting a picture. Does that look good over there? How about like this?
And frankly, exploratory programming is more fun than fixed-problem programming.
F# has this wonderful ability to cross-cut the existing .NET libraries to create ad-hoc solutions. Instead of being concerned with structure so much you think more about results. The users. Big pieces of work get done in very few lines of code — and I’ve found that fewer lines of code mean less bugs.
There’s another subtle reason FP is so much fun. Because of the way it works, testing doesn’t have the same role. FP, at it’s heart, is about transforming data. It’s like writing an entire program as one big honking SQL statement — you just select from here, compare with that, insert over there. As such, there’s not a lot of room for error to creep in. It’s either right or it’s wrong. In a true FP, unit testing is not the nirvana it is in OOP. The tricky part, of course, is getting your data structures optimized for the transforms you want. And to aggressively refactor everything you write until it’s tight. But when it works, it hums. It really sings. It all comes together for a really nice programming experience.
Fun stuff!If you've read this far and you're interested in Agile, you should take my No-frills Agile Tune-up Email Course, and follow me on Twitter.