8 April 2010

Agilish programming and haskell type classes

I'm drinking a lovely bottle of wine. So time to write a little about a little programming technique.

By agilish I'm referring to no specific agile development platforms, but to the general idea of "Shall we plan everything out first? Bloody hell no, let's get cracking! Also lets value individuals over processes and stuff *hic*".

An important aspect of some agile development methodologies is to immediately implement what the customer things is most important: these styles encourage the developers simply to work out what the most important feature is (features being stuff the customer can actually see doing something useful, 'seeing' being the important thing), implement it and then repeat: for small projects this is good!

However the relative lack of planning can cause problems:

So say you're an object oriented programmer implementing a GUI library, one feature after another. This is a class that you might provide for a widget*:

class Widget:
   Hi! I'm a class providing a widget. Clicking me executes method clicked!

   method add:
      Hello there, this is what happens when I'm added to the window!

   method clicked:
      Hello there, I'm what clicking me does!

* if you don't study design patterns that is, but this is beside my point

And then you implement some GUI programs until you realize you need to be able to remove widgets for another particular program, so you alter your code:


class Widget:
   Hi! I'm a class providing a widget. Clicking me executes method cliked!

   method add:
      Hello there, this is what happens when I'm added to the window!

   method clicked:
      Hello there, I'm what clicking me does!

   method remove:
      Hello there, I've been removed!

To do this they modified existing code which you may also introduce errors into the previously good code (like cliked). Even a syntactic error wastes development time.

I mentioned design patterns above: relying on the work of others (especially for basic examples like this) is very important, but you're always going to encounter new problems, or even old ones you don't recognize. What you need to do is have a more supportive paradigm for your 'add features one at a time when they are directly required!' style of coding. Enter type classes.

First, for the uninitiated, a little explanation. Type classes describe a selection of functions which can be performed on a data type. They are a little like java interfaces, except they can contain code. Similarly, they are like C++ virtual classes, but Haskell, not being an object oriented language has no receiver.

Importantly, unlike Java interfaces, you can declare a Haskell data type to be an instance of a type class without having to alter the file containing the new instance.

Here is a definition of a simple, one function, type class

class Eq foo where
   (==) :: foo -> foo -> Bool

This describes the class Eq (I call it Equality). The class has one parameter, foo. It provides one function (==) which takes two foos and 'returns' a a boolean True or False. The brackets surround (==) to show it is an infix function: eg 1 == 2, rather than (== 1 2).

I am not intending to write a Haskell tutorial; there are many good books and tutorials available; I'm just trying to provide enough information so uninformed readers might see the benefits of programming in this style, not necessarily in Haskell.

So! A style I prefer to use with type classes is to implement only one feature with each type class. For example, compared with the above we might get

class AddableWidget w where
   -- Hello, I'm a widget that can be added
   add_widget :: w -> IO () -- IO () indicates an input output may be performed during the function, the () shows that there is no result 'returned'.
      -- I've been added to the GUI!

class ClickableWidget w where
   -- Hello, I'm a widget that can be clicked
   clicked :: (Int, Int) -> w -> IO ()
      -- I've been clicked

You can keep these as part of one module, and then if you realize you need to remove your widgets then in another module you may add:

class RemoveableWidget w where
   -- Hello, I'm a widget that can be removed
   remove :: w -> IO ()
      -- I've been removed

So you don't have to alter your existing code. That is something difficult to do with traditional strongly typed classes (difficult at least, without a bit of forethought which I am assuming has not taken place ;)

Not so difficult in ruby and smalltalk, with their open classes, however the differences between untyped and strongly typed languages are a whole other kettle of fish.

Maybe tomorrow I might provide an example of how to perform this with a strongly typed object oriented language.
Delve was meant to answer that question.... whenever will I get round to finishing it.

No comments:

Post a Comment