I realize that this could potentially be considered a subjective or maybe an off-topic question, so I hope that rather than have it closed it would get migrated, maybe to Programmers.
I'm starting to learn Haskell, mostly for my own edification, and I like a lot of the ideas and principles backing the language. I became fascinated with functional languages after taking a language theory class where we played around with Lisp, and I had been hearing a lot of good things about how productive Haskell could be, so I figured I'd investigate it myself. So far, I like the language, except for one thing that I can't just get away from: Those mother effing function signatures.
My professional background is mostly doing OO, especially in Java. Most of the places that I've worked for have hammered in a lot of the standard modern dogmas; Agile, Clean Code, TDD, etc. After a few years of working this way, It has definitely become my comfort zone; especially the idea that "good" code should be self documenting. I've become used to working in an IDE, where long and verbose method names with very descriptive signatures are a non-issue with intelligent auto completion and a huge array of analytical tools for navigating packages and symbols; if I can hit Ctrl+Space in Eclipse, then deduce what a method is doing from looking at its name and the locally scoped variables associated with its arguments instead of pulling up the JavaDocs, I'm as happy as a pig in poop.
This is, decidedly, not part of the community best practices in Haskell. I've read through plenty of different opinions on the matter, and I understand that the Haskell community considers its succinctness to be a "pro". I've gone through How To Read Haskell, and I understand the rationale behind a lot of the decisions, but it doesn't mean that I like them; one letter variable names, etc. aren't fun for me. I acknowledge that I'll have to get used to that if I want to keep hacking with the language.
But I can't get over the function signatures. Take this example, as pulled from Learn you a Haskell[...]'s section on function syntax:
bmiTell :: (RealFloat a) => a -> a -> String
bmiTell weight height
| weight / height ^ 2 <= 18.5 = "You're underweight, you emo, you!"
| weight / height ^ 2 <= 25.0 = "You're supposedly normal. Pffft, I bet you're ugly!"
| weight / height ^ 2 <= 30.0 = "You're fat! Lose some weight, fatty!"
| otherwise = "You're a whale, congratulations!"
I realize that this is a silly example that was only created for the purpose of explaining guards and class constraints, but if you were to examine just the signature of that function, you would have no idea which of its arguments was intended to be the weight or the height. Even if you were to use Float
or Double
instead of any type, it would still not be immediately discernible.
At first, I thought I would be cute and clever and brilliant and try to spoof it using longer type variable names with multiple class constraints:
bmiTell :: (RealFloat weight, RealFloat height) => weight -> height -> String
This spat out an error (as an aside, if anyone can explain the error to me, I'd be grateful):
Could not deduce (height ~ weight)
from the context (RealFloat weight, RealFloat height)
bound by the type signature for
bmiTell :: (RealFloat weight, RealFloat height) =>
weight -> height -> String
at example.hs:(25,1)-(27,27)
`height' is a rigid type variable bound by
the type signature for
bmiTell :: (RealFloat weight, RealFloat height) =>
weight -> height -> String
at example.hs:25:1
`weight' is a rigid type variable bound by
the type signature for
bmiTell :: (RealFloat weight, RealFloat height) =>
weight -> height -> String
at example.hs:25:1
In the first argument of `(^)', namely `height'
In the second argument of `(/)', namely `height ^ 2'
In the first argument of `(<=)', namely `weight / height ^ 2'
Not understanding completely why that didn't work, I started Googling around, and I even found this little post that suggests named parameters, specifically, spoofing named parameters via newtype
, but that seems to be a bit much.
Is there no acceptable way to craft informative function signatures? Is "The Haskell Way" simply to Haddock the crap out of everything?
newtype
" approach. Especially for this sort ofwidth/height
problem. It keeps code clear and more maintainable and strongly aids in dimensional analysis. If you do it right the boilerplate is minimal, type signatures are more useful, and bugs are less frequent. – John L(RealFloat weight, weight ~ height) => ...
– Sarah