I’ve been looking for a way to express how clever (too clever?) Haskell is. The combination of types, pattern matching, functional constructions, and abstract thinking (stopping just short of calling it ‘math’) allows all kinds of manipulations, which inspire the same feeling of awed appreciation in me than math proofs did back in my university days.
One example which illustrates this beautifully is Lenses. Lenses are a way of ‘focusing’ on a particular component of a data structure (records, maps, other). Focusing can mean viewing, modifying (as in returning a new data structure of course), converting, setting the component to a new value. When composing lenses you can elegantly perform the same actions on nested data structures.
Let’s go with an example. Say I have a home automation system, with variables for every room.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
This example is based on a real-life system I saw in a friend’s house – the friend’s husband is an industrial electrician and had rigged it all up himself (me = impressed and a little jealous).
Notice the _ on the names of the record attributes, this is used by the makeLenses to make corresponding lenses for the attributes (makeLenses will generate a lens name for _name)
We can focus on the name given to the house. name is a lens, and has type Lens House String
which is not hugely useful, since we can just get the name of the record by doing _name h. Though we can also set it or calculate over it
It becomes more useful when we compose lenses (livingroom of House and tInF of Room) to get or affect, say, the temperature of the living room.
1 2 3 4 5
But wait, I have an american friend who’s visiting and would prefer to see the temperature in Fahrenheit. No problem, let’s make a lens which will take the temperature of the Room and convert it to F for us, as well as convert it back to C when going back to the Room. (cToF and fToC are what you’d expect)
1 2 3 4 5 6 7 8 9 10 11 12 13
Then you’ve got Traversals, which can focus on more than one element. Say we want to command (switch on/switch off) everything in the house, heating and lights. We start at room level, and compose our way up:
1 2 3 4 5 6 7 8 9 10 11 12 13
To view the values in this case is a little more complex, since we have more than one. There are 2 ways: either see the values as a list, or combine them in a meaningful way by defining a monoid (mempty and mappend).
1 2 3 4 5 6 7 8 9
example code here
Also in the lens library:
- Prisms are a special case of traversals (as are lenses) – see this link for a nice explanation.
- Isomorphisms are connections between types that are equivalent – mappings between those types, such that forward mapping followed by backward mapping (and the reverse) comes down to the identity function.
- shorthand notations for view, set, etc and many more convencience functions besides, which could probably be handy when you use lenses all the time but which I don’t find very readable as a newcomer to lenses.
- I also have to add that I’m using the simplified Lens’ and Traversal’, there are more general Lens and Traversal types which take more arguments, but my brains were already dribbling out of my ears so I’ll save us the full-on power version for another day.
Lenses, Traversals, Prisms et al are not limited to records, however, you can also take a look at – say you want a traversal of the bits of an Int:
The lens library introduces some convenience functions for common data structures
1 2 3
You can focus on the bits in an Int, or a node in a Tree, etc etc. And that’s not even considering other lens libraries (say, hexpat-lens for nodes in an HTML page for scraping).
The great thing, all this “magic” (endless composability) is actually a result of fairly straightforward, though very clever, function composition! I recommend watching this Skillsmatter video featuring Simon Peyton Jones explaining how lenses work:
(note: I would have embedded, but Skillsmatter apparently requires you to log in now, unfortunately)
If you have to stop and think, don’t worry, this is normal. Even though the explanation is well put together, I had to pause and ponder a few times.
I’ll get back to the main theme of this post. Someone told me that you don’t just learn haskell in the usual way (by programming), you study Haskell – and also that Haskell isn’t for everyone.
The first bit, the fact that you have to study it is also my experience – sure, you can get things done when you know the syntax, but it takes more than that to do it elegantly.
My feeling is that you have to have at least passing familiarity with the building blocks you can use – functors, applicative, monads, monoids – and the multiple ways to combine them. Those building blocks allow you to combine types and functions using those types in particular ways. And I’m not even touching on the multiple extensions to Haskell, which will change/add to the language itself (e.g. Template Haskell, allowing you to fairly easily define DSLs, to name but one).
For me, this has the following consequences:
- after one year, I still feel like I’ve a lot to learn
- Haskell will probably never have a completely mainstream community
- conversely, companies using Haskell will attract a certain type of people and (may) move in certain more advanced problem domains
- people who have learned Haskell can be quite smug, and – dare I say – a touch pedantic. Though there are plenty of nice people too, Simon Peyton Jones (see video above) blows me away by his humility and clarity, showing once again that the smartest people don’t feel the need to be heavy-handed about it.
It would be nice to see Haskell become more common – making it as accessible as humanly possible is probably the first step, since it can already be challenging enough as it is. We can do this by creating good docs, reasonably clear blog posts, and a having friendly, forgiving attitude. I’m willing to do my bit.