HiveBrain v1.2.0
Get Started
← Back to all entries
patternModerate

My Haskell word count program

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
countwordprogramhaskell

Problem

Here's my first significant Haskell program - it presents a count of each word seen on stdin. Any comments are welcome.

import Data.Map

emptyMap = empty :: Map String Integer

countWord m s = alter (\x -> case x of
                               Nothing -> Just 1
                               Just n -> Just (n+1)) s m

mapPairs m = concat [ k ++ ": " ++ (show v) ++ "\n" | (k,v) <- toList m ]

mapTotal = sum . elems

mapOutput m = (mapPairs m) ++ "Total words: " ++ (show (mapTotal m)) ++ "\n"

wc = interact $ mapOutput . (foldl countWord emptyMap) . words


Some of the questions I have are:

-
Am I using Map efficiently here? (I realize there are other solutions which do not use a Map - the main point of this exercise was to learn how to use Data.Map.)

-
How can I make sure that emptyMap, countWord and mapTotal all use the same integral type (Int vs. Integer)? For instance, the type of mapTotal is Map a Integer -> Integer, and so I'll get a type error if I change the definition of emptyMap to empty :: Map String Int. Ideally I'd like to make the choice between Int and Integer in one place for all three definitions.

Solution

Your usage of Data.Map is sound. I see nothing that could be improved as long as you stay with Data.Map.

If you give all of the functions explicit type declarations, so that the Monomorphism Restriction doesn't apply, you can use generic integral types:

mapTotal :: Integral i => Map a i -> i


Some quick style-related things you could change:

First:

(\x -> case x of
  Nothing -> Just 1
  Just n -> Just (n+1))


... is the same as

Just . maybe 1 (+1)


Second:

You don't need parens around many of your function calls; if you have something like a +.- (foo bar baz) ¤: b, it means the same thing as a +.- foo bar baz ¤: b because every operator has lower precedence than function application.

Third:

You use very long indentations. A tip is to insert newlines after = and before control structures like case to make the code more compact. I find this style to be quite good.

Code Snippets

mapTotal :: Integral i => Map a i -> i
(\x -> case x of
  Nothing -> Just 1
  Just n -> Just (n+1))
Just . maybe 1 (+1)

Context

StackExchange Code Review Q#8904, answer score: 12

Revisions (0)

No revisions yet.