strings – Word frequency finder

Challenge: find the top three most frequently used words in a string. Only imports from the base package are allowed (source: codewars)

I’m looking for feedback on (1) readability, and (2) performance

  • Was folding the string into “frequency map” a good choice?
  • I have several small functions that I compose together. Are these
    abstraction layers helping or hurting readability?
  • How are my functions names?
  • Does the base package have any libraries that would make this code significantly easier to write?
  • Could I benefit from using more pointfree style?
import qualified Data.Map as Map
import Data.List
import Data.Function
import Data.Char
import Data.Maybe

top3 :: (Char) -> ((Char))
top3 str =
  let
    wordFrequencyMap = foldr (Map.alter increment) Map.empty (normalizedWords str)
    sortedWordFrequencies = sortBy reverseBySecond (Map.toList wordFrequencyMap)
  in map fst (take 3 sortedWordFrequencies)
  where
    normalizedWords = filter containsAtLeastOneAlphaNumericChar . lowerCaseWords . map isLegalChar
    containsAtLeastOneAlphaNumericChar w = find isAlphaNum w /= Nothing
    lowerCaseWords = map (map toLower) . words
    isLegalChar c = if isAlphaNum c || c ==''' then c else ' '
    reverseBySecond x y = if snd x < snd y then GT else LT
    increment Nothing = Just 1
    increment (Just x) = Just (x + 1)