Could someone provide a link to a good coding standard for Haskell? I've found this and this, but they are far from comprehensive. Not to mention that the HaskellWiki one includes such "gems" as "use classes with care" and "defining symbolic infix identifiers should be left to library writers only."
And HLint http://hackage.haskell.org/package/hlint
Wei Hu
2009-12-31 04:34:22
I really like this answer, but can you provide some more code samples? I'm still not fully acquainted with Haskell lingo, so the "Function application binds tighter than any infix operator" and a few other points leave me confounded.
CaptainCasey
2010-01-05 07:42:19
@CaptainCasey: I started to add some examples, but then the answer got way too long and hard to read. It's meant as a short set of suggestions; if it's to turn into a real style guide, it will have to be done by somebody else.But let me know your specific points. The binding tightness just means that `(length l) + 1` is ugly. The application of `length` automatically binds tighter than the application of `+`, so the idiomatic thing to write is `length l + 1`.Parentheses are the bane of functional programs.
Norman Ramsey
2010-01-06 04:42:05
about:`Format your code so it fits in 80 columns.` I prefer 120 col anymore.. nothing ever seems to fit in 80.
Talvi Watia
2010-07-09 04:11:14
+3
A:
I like to try to organize functions as point-free style compositions as much as possible by doing things like:
func = boo . boppity . bippity . snd where boo = ... boppity = ... bippity = ...
I like using ($) only to avoid nested parens or long parenthesized expressions
... I thought I had a few more in me, oh well
jberryman
2009-12-31 20:51:05
+7
A:
Some good rules of thumbs imho:
- Consult with HLint to make sure you don't have redundant braces and that your code isn't pointlessly point-full.
- Avoid recreating existing library functions. Hoogle can help you find them.
- Often times existing library functions are more general than what one was going to make. For example if you want
Maybe (Maybe a) -> Maybe a
, thenjoin
does that among other things.
- Often times existing library functions are more general than what one was going to make. For example if you want
- Argument naming and documentation is important sometimes.
- For a function like
replicate :: Int -> a -> [a]
, it's pretty obvious what each of the arguments does, from their types alone. - For a function that takes several arguments of the same type, like
isPrefixOf :: (Eq a) => [a] -> [a] -> Bool
, naming/documentation of arguments is more important.
- For a function like
- If one function exists only to serve another function, and isn't otherwise useful, and/or it's hard to think of a good name for it, then it probably should exist in it's caller's
where
clause instead of in the module's scope. - DRY
- Use Template-Haskell when appropriate.
- Bundles of functions like
zip3
,zipWith3
,zip4
,zipWith4
, etc are very meh. UseApplicative
style withZipList
s instead. You probably never really need functions like those. - Derive instances automatically. The derive package can help you derive instances for type-classes such as
Functor
(there is only one correct way to make a type an instance ofFunctor
).
- Code that is more general has several benefits:
- It's more useful and reusable.
- It is less prone to bugs because there are more constraints.
- For example if you want to program
concat :: [[a]] -> [a]
, and notice how it can be more general asjoin :: Monad m => m (m a) -> m a
. There is less room for error when programmingjoin
because when programmingconcat
you can reverse the lists by mistake and injoin
there are very few things you can do.
- For example if you want to program
- When using the same stack of monad transformers in many places in your code, make a type synonym for it. This will make the types shorter, more consice, and easier to modify in bulk.
- Beware of "lazy IO". For example
readFile
doesn't really read the file's contents at the moment the file is read. - Avoid indenting so much that I can't find the code.
- If your type is logically an instance of a type-class, make it an instance.
- The instance can replace other interface functions you may have considered with familiar ones.
- Note: If there is more than one logical instance, create newtype-wrappers for the instances.
- Make the different instances consistent. It would have been very confusing/bad if the list
Applicative
behaved likeZipList
.
yairchu
2010-01-06 00:20:37