Encoding text to keypresses on old phone keyboard (0-9*#) I am going through "Haskell Programming from first principles" for the first time and I would like to get some pointers on what can be improved in my code, which is the following: 

module DaPhone ( stringToKeyTaps
           , fingerTaps
           , mostPopularLetter
           , mostPopularWord
           , coolestLtr
           , coolestWord
           , daPhoneTaps
           , daPhoneTaps
           , testKeyBoard
           , testConvo) where

import Data.Char
import Data.List
import Data.Maybe

type KeyLabel = Char
type Digit = Char

data Key = Key KeyLabel [Digit] deriving (Eq, Show)
label (Key l _) = l
digits (Key _ ds) = ds

isDigitOf :: Digit -&gt; Key -&gt; Bool
c `isDigitOf` key = c `elem` (digits key)

type KeyboardLayout = [Key]

getKeyFor :: KeyboardLayout -&gt; Digit -&gt; Key
getKeyFor kmap c = fromJust $ find (c `isDigitOf`) kmap


type Taps = Int
type KeyTaps = [(KeyLabel, Taps)]

stringToKeyTaps :: KeyboardLayout -&gt; String -&gt; KeyTaps
stringToKeyTaps _ [] = []
stringToKeyTaps kmap (c:cs)i
    | c `elem` ['A'..'Z'] =
    charToKeyTap kmap '^' : stringToKeyTaps kmap ((toLower c):cs)
    | c `elem` charSet = charToKeyTap kmap c : stringToKeyTaps kmap cs
    | otherwise = stringToKeyTaps kmap cs
    where charSet = foldr ((++) . (digits)) [] kmap

charToKeyTap kmap c =
        (label $ theKey, (fromJust $ elemIndex c $ digits $ theKey) + 1)
                        where theKey = getKeyFor kmap c

fingerTaps :: KeyTaps -&gt; Taps
fingerTaps = foldr ((+) . snd) 0

mostPopularLetter :: String -&gt; Char
mostPopularLetter = mostPopularElement

mostPopularWord :: String -&gt; String
mostPopularWord = mostPopularElement . words

coolestLtr :: [String] -&gt; Char
coolestLtr = mostPopularLetter . filter (/=' ') . concat

coolestWord :: [String] -&gt; String
coolestWord = mostPopularWord . intercalate " "

mostPopularElement :: (Ord a, Eq a) =&gt; [a] -&gt; a
mostPopularElement = head . head .
        sortBy (\x y -&gt; (length y) `compare` (length x)) . group . sort




-- provided variables
testKeyBoard :: KeyboardLayout
testKeyBoard = [ Key '1' "1"
               , Key '2' "abc2"
               , Key '3' "def3"
               , Key '4' "ghi4"
               , Key '5' "jkl5"
               , Key '6' "mno6"
               , Key '7' "pqrs7"
               , Key '8' "tuv8"
               , Key '9' "wxyz9"
               , Key '0' " 0"
               , Key '*' "^"
               , Key '#' ".,"
               ]

testConvo :: [String]
testConvo = [ "Wanna play 20 questions"
            , "Ya"
            , "U 1st haha"
            , "Lol ok. Have u ever tasted alcohol lol"
            , "Lol ya"
            , "Lol ur cool haha. Ur turn"
            ]

--partially applied converter for provided exercise
daPhoneTaps = stringToKeyTaps testKeyBoard


I would love to get a code review by someone better acquainted to functional programming and Haskell.
Library combinators and list comprehension syntax allow much collapsing of your code, and many of your type aliases and oneliners do not deserve a name, except perhaps for those at the bottom because you export them.

type KeyboardLayout = [(Char, [Char])]

stringToKeyTaps :: KeyboardLayout -&gt; String -&gt; [(Char, Int)]
stringToKeyTaps kmap cs =
  [ (label, index + 1)
  | c &lt;- cs
  , c' &lt;- ['^' | isUpper c] ++ [toLower c]
  , Just (label, index) &lt;- asum $ (map . traverse) (elemIndex c') kmap
  ]

fingerTaps :: [(Char, Int)] -&gt; Int
fingerTaps = sum . map snd

mostPopularElement :: (Ord a, Eq a) =&gt; [a] -&gt; a
mostPopularElement = head . head . sortOn length . group . sort