I’ve been loving the Haskell trail so far, having a great time working my way through the exercises. I’ve made my way through half of Learn you a Haskell in the last year, so these exercises have been a great way of learning my knowledge (or lack thereof to the test). As a general point of feedback, I have so far found the exercises to be right at the limit of my mental capabilities - I feel I am literally stretching my mind as I’m working through them, just scraping across the line to finish the exercise, but learning a lot on the process.
I’ve made it as far as the Pointfree Style exercise and am now throwing my hands in the air. Here’s the one that got me:
isDigit :: Char -> Bool
isDigit x = elem x "1234567890"
How on God’s green earth am I able to rewrite this in pointfree style?
I understand that, in effect, I am trying to:
Produce a function, that when applied, calls elem with its first parameter and 1234567890 as its second parameter.
I have no idea how I would even begin doing that. As I understand currying, functions are built left to right. eg:
something :: a -> b -> c
Is a function that takes a as its first argument, and returns a function that then takes b as its second argument, producing c.
In effect I am trying to return a function that takes an a and applies it to b. Is this even possible?
Is a function that takes a as its first argument, and returns a function that then takes b as its second argument, producing c.
Close!
It’s a function that takes a as its only argument and returns a function that takes b as its only argument. All fns in Haskell take only a single argument.
While you can get rid of ($)s by adding parenthesis, you can’t do the same with (.). If you wanted to translate the point-free versions to “Lispy”, you’d have to do it like this:
Here, you can see they aren’t actually that similar, which is why the point-free versions work out differently.
In the first example, the ($) is actually redundant. Function application binds tightest in Haskell, so just putting all and isUpper next to each other is enough to form a function that accepts a list and returns True if all elements are upper-case. This can then be composed with any using (.).
hasUppercaseWord = any . all isUpper
This is similar to something like:
secondElement = head . drop 1
In the second example, words is not an argument to reverse – that wouldn’t make sense. So there’s no application (implied or made explicit by ($)), you just compose each function with (.).
Brilliant answer, @pat. Thank you - this helps a lot.
As a point of general feedback, I feel I struggle with the pointfree exercises because I can’t clearly see with my eyes where each function ends and begins (eg. what’s an argument and what’s the next function) because the function names are all quite unfamiliar to me. I guess this is just a familiarity thing that will go away with time!
I’ve bought Maybe Haskell last night, and this is helping a lot more too. In all honesty, I feel as if I skimmed over too much of Learn you a Haskell as it was perhaps more dense than I gave it credit for. I’m paying for that now.