[Haskell-cafe] PrefixMap: code review request

Brian Hulley brianh at metamilk.com
Mon Feb 27 17:54:16 EST 2006


David F.Place wrote:
[snip]
> partList :: Ord k => [([k],v)]->[k]->[(k,[([k],v)])]
> partList pairs alphabet = reverse . fst $ foldl' f ([],pairs) alphabet
>     where f (result,pairs) l = (result',rest)
>               where (part,rest) = span ((==l) . head . fst) pairs
>     result' = if null part
>          then result
>          else (l,part):result

If the above code is put into a non-literate file as:

 partList :: Ord k => [([k],v)]->[k]->[(k,[([k],v)])]
 partList pairs alphabet = reverse . fst $ foldl' f ([],pairs) alphabet
     where f (result,pairs) l = (result',rest)
               where (part,rest) = span ((==l) . head . fst) pairs
     result' = if null part
          then result
          else (l,part):result

there is a parse error (using ghc) at the line beginning with result'. This 
binding doesn't line up with anything. Also the second 'where' is 
dangerously close to the column started by the 'f' after the first 'where' 
(may not be noticeable in this email due to whatever font it is being 
displayed in but it's only indented by one space) which makes it a bit 
tricky to read.

I suggest:

partList :: Ord k => [([k],v)]->[k]->[(k,[([k],v)])]
partList pairs alphabet = reverse . fst $ foldl' f ([],pairs) alphabet
    where
       f (result,pairs) l = (result',rest)
          where
             (part,rest) = span ((==l) . head . fst) pairs
             result' = if null part
                              then result
                              else (l,part):result

or:

partList :: Ord k => [([k],v)]->[k]->[(k,[([k],v)])]
partList pairs alphabet = reverse . fst $ foldl' f ([],pairs) alphabet
    where
       f (result,pairs) l = (result',rest)
          where
             (part,rest) = span ((==l) . head . fst) pairs
             result' =
                if null part
                   then result
                   else (l,part):result

because always starting an 'if' construct on a new line ensures that you 
will never ever have any problems with it's layout (especially helpful for 
people used to C) when you use it in a 'do' block.

Also, both the above variants ensure that your code can be edited using 
variable width fonts, any tabbing regime you like (as long as leading 
whitespace only has tabs and never spaces), and will be immune to identifier 
renamings. The golden rule for 'safe' layout is always to start a layout 
block on the next line after 'do' 'where' 'of' 'let' and to try to resist 
the tempation to save a line by squashing the first binding onto the same 
line as the 'let' etc.

The second variant has the added advantage that the horizontal indentation 
is kept under control (eg in the above all indenting is 3 spaces further in) 
whereas when people write things like

if p == q then let a = 4
                          b = if a ==4 then let q = 2
                                                         s = 56

after only 3 indents the code is already half way across the screen (not to 
mention the fact that the above layout is completely brittle and can be 
destroyed by a simple search-and-replace op to change 'q' to 'hello')

Of course all the above is just easy-to-talk-about technicalities of layout 
and you were really interested in getting feedback about idiomatic usage - 
hopefully someone else will give some feedback about that (I'm too lazy...) 
:-)

Regards, Brian.




More information about the Haskell-Cafe mailing list