I've been playing around with Haskell on and off for a while now. I guess I wrote my first Haskell program around 2005. It's been pretty slow going but I've started to get used to it more and more, and it has replaced Python as my main language for side projects alongside Java.
One of the great things about the Haskell community is that it is continually trying to find more elegant, more efficient and more powerful ways of solving problems. Over the years this has resulted in a large body of knowledge that seems to be growing by leaps and bounds every month.
For a beginner though, even a long time beginner like myself, it can be intimidating and it can discourage you from learning more about this wonderful language. You might feel that if your program that doesn't use the hottest techniques, idioms, and libraries then it will be obsolete before it's even completed.
The fact is though, that even if you write fairly bad Haskell, you will still learn a lot and you will learn even more refactoring that code as you go on. Despite all of the sophisticated new concepts that people are developing in Haskell, the basic language is still incredibly powerful and expressive, and this is easy to miss if you are following the cutting edge developments.
As a response I purposely try to write elementary Haskell, and this helps me write more Haskell programs. When programming elementary Haskell:
- Use simple data structures. Plain records, union types, and containers.
- Make specific data types first, parameterization can be done later
- You probably don't need your own typeclasses yet
- When in doubt, implement it in IO first and pull out pieces of pure code
- Use wrapper types so you can change data structures later, e.g. data BookCollection = BookCollection [Books]
- Use wrapper types to decouple code, especially to decouple application logic from complicated libraries
- Stick to the plain IO monad, avoid monads like State,Reader, and Writer for pure code
- Use IO to decouple code if needed.
Some extra things I've been trying to work on for general readability but haven't been doing enough of yet are:
- Limit the number of direct dependencies a module has, use the number of imports as a guide
- Isolate special language features in sub modules, see if the part that is needed can be wrapped in a simpler interface
- Prefer qualified imports
Two side projects that I've been trying to use this on are:
And a haskell editor.
I ended up wanting to create the editor after noticing that I really just wanted a tabbed editor for haskell files and found that emacs buffer switching was just encouraging me to make larger and larger modules. If I can integrate it with command line applications through shelly then I should be ready to start using it regularly.
Eventually I might outgrow this practice but I think it results in code that strikes a good balance between taking advantage of Haskell's expressiveness while being fairly easy to understand and hopefully even easy to train other people to work with. I probably need to work in lens for easier data manipulation and pipes or conduit to manage resources but so far it seems like I can still build some fun and enjoyable programs in this limited space.