Showing posts with label ruby. Show all posts
Showing posts with label ruby. Show all posts

Thursday, July 3, 2008

Why Functional Programming?

Earlier this week Robert Fischer presented a session at the local Ruby group called "The OCaml Guide for Rubyists" (slides and video available soon). During Q&A, the last question was a simple, "Why?" Not a question so much as a lamentation. The real question being asked was, "Why in the world would you want to put up with horrible syntax, bizarre compiler errors, and unreadable type system REPL output just to accomplish the same thing I do regularly with a few lines of Ruby?" Robert's short answer was, "It will warp your mind," which might not have convinced anyone that OCaml was worth their time. I'd have given the same answer, though, only I would have made my response long, meandering, and possibly in need of a good edit. Welcome and let the journey begin!

I love programming because of the abstraction. Finding and removing repetition is a joyful act. In my C days (which were thankfully short, for both me and my customers), the abstraction came from carefully defined modules. The pinnacle of design was a finely crafted global library accessible to anyone who cared to include the proper header and debug the resulting link error. Entities and concepts were modeled in their own .c files, hierarchies and relationships were defined through directory structures, and exported abstractions made available in .lib files. Oh yeah, I really had this down, and I knew what programming was all about (including spending days finding memory leaks).

And when someone showed me C++ a lightbulb turned on. My basic unit of abstraction changed from the library to inheritence, and my mind was warped. Holy cow! Modeling everything as parent-child relationships opened up a whole new realm of software design. Everything is an object, and everything has shared code in its parent type. I could get wide-spread behavior changes in my program simply by changing some code way up high in the type hierarchy. And the way to do this was multiple inheritance, right? All my systems looked like inverted pyramids... there was one core implementation class with dozens of parent classes spread out above it. Genius, or so I thought.

And then I found out about the Gang of Four Design Patterns. In an act of supreme hubris, I remember arguing with the team architect about the definition of an interface. I firmly believed an interface was an interaction library, like the Win32 Application Programming Interface. I had no idea why anyone would create a parent type with no implementation, and thank you Gavin Ford (wherever you are) for handing me that book and telling me to bugger off. With design patterns and a new found mandate to prefer composition over inheritance, I was off and running with a primary unit of abstraction: the class. Mind warp #2, for those counting. I could never go back to C now.

And for a long time it stopped. Java and its awesome toolset let me create reams and reams of code all structured to one design pattern or another. And the Java libraries contained many of the patterns, so it must be the right way to design software. Java enabled me to overuse patterns, and I slowly realized that they weren't an end to themselves... but this didn't really qualify as a mind warp despite the hundreds of Internet posts proclaiming the death of patterns due to closures and dynamic typing.

People moved on. Some moved on to embrace meta-programming as the next mind warp. But is this really a means of abstraction? Does meta-programming allow you to construct more general and reusable structures? From a client perspective, writing code against ActiveRecord and GORM is certainly a time saving shortcut. But in my experience, meta-programming exposes you (as a writer) to a very low level of the language. Method dispatch, metaclasses, caching closures... this doesn't feel very abstract. It feels reusable as long as method calls and database columns and xml schema all line up. (Quick quiz, what's the difference between programming by convention and coincidence? One is the next big thing and the other is warned against in the Prag book.) But is metaprogramming layer of abstraction that allows you to specify behavior in a more general way? Perhaps not. It's a mind warp, but a very implementation specific warp.

Another direction people move towards is device development, most notably the iPhone. I spent waaay too long programming mobile devices and I cringe at the thought of downloading some iPhone SDK. Honing my programming skills towards learning a device API and graphics library is simply the wrong direction. It is a step closer to the hardware, the implementation, and a step away from a higher level of abstraction. No thanks. I'm quite skeptical that new software ideas will be the product of anyone moving into the device development space. Rediscovering pointer arithmetic and reimplementing existing frameworks on a smaller footprint seems a more likely outcome. Neither of which sounds appealing.

So I'm here to say that mindwarp #3 is discovering the function as the basic unit of abstraction. Jaw-droppingly beautiful abstractions and generalizations can be created out of just functions. You can rediscover the usefulness of partial functions and currying, which were techniques created in the 1800s. You can be in the direct lineage of Alan Turing, who used higher order functions in the 1930s to define his theoretical Turing Machine in his paper "On Computable Numbers, with an Application to the Entscheidungsproblem." And you can finally understand recursion in a deep and intuitive way, and you'll feel like you've looked into the abyss and somehow come back to tell everyone else about it. And maybe, just maybe, you can explain to me what a freakin' monad is.

And maybe (just maybe!) Ruby isn't the language to try this in. Ruby is awesome for a lot of things. Just check out how fast and furious one-line responses to Cedric's challenge appeared. The fact the Ruby solutions are denigrated as "a dime a dozen" says something about how fast and easy the language is to work in. But how easy are function objects in Ruby? Does it take 5 pages of text to fully explain the fine points of procs and blocks? I've been told several times to stop trying to do FP in Groovy because a better functional programming language will more easily reveal the beauty and simplicity of functions. You want to concentrate on the FP concepts and eliminate all the noise of how the language implements functions. Non-functional languages contain imperative and procedural noise that will distract you from having your mind warped. Just pick a more functional language.

And that is my answer of why Rubyists should learn OCaml: Your mind will warp when you discover functions are the next unit of abstraction, and OCaml's sort-of FP purity will have your mind warping sooner than it would with other languages.

Thus concludes my contribution to a well covered topic. Interested readers may wish to read the original Why Functional Programming Matters and Raganwald's thoughts on the same.

Thanks!