Wrapping up Clojure
I really enjoyed what i learned about Clojure last week. I notice an interesting thing: if i don’t do the exercises, i feel as if i don’t like the language. But when i actually try them out and solve a few problems for myself, i find i like it.
I like Clojure but i think that’s really because i like Lisp. For me, Clojure just seemed to add a bunch more keywords that i had to remember. Do i really need defprotocol and defrecord, just because they map to Java interfaces and types? I’d rather do without them. And the lack of support for automatic tail-optimised recursion disappointed me. Sure, you can do it explicitly … and that introduces another two Clojure keywords.
I want to get back to the day 3 exercises at some point, but Seven Languages in Seven Weeks is fast-paced and before i know it we’re into the last language!
And now, Haskell
I somehow feel that the whole book has been leading to this. We’ve seen functional elements in most of the languages, but now we get to a language with no excuses, no exceptions. It is uncompromisingly functional. And i love it.
I like strong typing, i like immutability, and i like the power of functions. Haskell is bringing them all together for me, as if it was my perfect dream language, created just for me! Well, i’m only just starting, so we’ll see if this perception continues :)
Just looking at the examples, and trying them out, i get an overall feeling of beauty in simplicity. Haskell is wonderful.
Filtering a list to just the even numbers in the list. The book gives an example using recursion:
allEven :: [Integer] -> [Integer] allEven  =  allEven (h:t) = if even h then h:allEven t else allEven t
The pattern match says that when you get to an empty list you return the empty list.
The tail-optimised recursion checks whether the head of the list is even, and if so includes it in the result, along with the even numbers of the rest of the list. Otherwise, it doesn’t.
We can also solve it with list comprehension. I first tried this:
allEven :: [Integer] -> [Integer] allEven list = [if even x then x else 0 | x <- list]
I was putting the check into the expression and actually replacing odd numbers with zeros. Not a great solution. Then i remembered we can put a filter on the input side to ensure that only even numbers go in. Suddenly it becomes beautifully simple:
allEven :: [Integer] -> [Integer] allEven list = [x | x <- list, even x]
Where Clojure felt cluttered with syntax, Haskell feels very minimal and concise.
Reversing a list
Well, this is too easy, so i expect they want me to do something more than just use the built in reverse keyword:
reverseList :: [Integer] -> [Integer] reverseList list = reverse list
I think recursion is going to be the way to go with this. Split the head and tail and stick them back in the other order. Now then, what’s wrong with this?
reverseList :: [Integer] -> [Integer] reverseList  =  reverseList (h:t) = reverseList(t):h
It gives a compile error:
Couldn't match expected type `Integer' with actual type `[Integer]' In the return type of a call of `reverseList' In the first argument of `(:)', namely `reverseList (t)' In the expression: reverseList (t) : h
It must have something to do with type checking when constructing a list. I wondered whether it needed a few more guards:
reverseList :: [Integer] -> [Integer] reverseList  =  reverseList [x] = [x] reverseList [x,y] = [y,x] reverseList (h:t) = reverseList(t):h
No. I think the problem is you can’t use this h:t style list construction with the way i’m imagining you can.
Whilst you can do this:
You cannot do this:
Basically, the head has to be an element, and the tail has to be a list.
However, we can use addition to join lists:
[2,3] ++ 
So the answer is:
reverseList :: [Integer] -> [Integer] reverseList  =  reverseList (h:t) = reverseList(t) ++ [h]
Enough for now
This is taking a long time and it’s probably getting boring for my readers!
I will try to do a bit more on Haskell and publish it to my github: sermoa/7languages7weeks/week7-haskell