Am I thinking functionally in these simple Haskell functions? I'm learning Haskell using the University of Pennsylvania's online materials. I'm a few lessons in and was looking for some feedback about whether I'm thinking functionally enough or porting over my Python background inappropriately.

Below are my answers to the problems set out in lesson three (these were someone's homework once but not any more, and were never mine!). Is my code below making any rookie mistakes for someone coming from another paradigm to functional?

import Data.List

-- Exercise 1
-- Take a list xs and an integer n, and return a list of every nth element of xs
everyNth :: [a] -&gt; Int -&gt; [a]
everyNth xs n = [snd x | x &lt;- (zip [1..] xs), fst x `mod` n == 0]

skips :: [a] -&gt; [[a]]
skips xs = map (everyNth xs) [1..length xs]

-- Exercise 2
-- Take a list of integers and produce a list of local maxima
triples :: [a] -&gt; [[a]]
triples (x:xs)
  | length (x:xs) &lt; 3 = []
  | otherwise = (x:take 2 xs) : triples xs

isMiddleMax :: (Ord a) =&gt; [a] -&gt; Bool
isMiddleMax (x:y:z:[]) = x &lt; y &amp;&amp; y &gt; z

localMaxima :: [Integer] -&gt; [Integer]
localMaxima xs = map maximum . filter isMiddleMax $ triples xs

-- Exercise 3
-- Take a list of integers 0-9 and return a histogram
groupSort :: (Eq a, Ord a) =&gt; [a] -&gt; [[a]]
groupSort xs = group $ sort xs

countInstance :: (Eq a, Ord a) =&gt; [a] -&gt; (a, Int)
countInstance xs = (head xs, length xs)

countInstances :: (Eq a, Ord a) =&gt; [a] -&gt; [(a, Int)]
countInstances xs = map (countInstance) $ groupSort xs

maxInstances :: [Int] -&gt; Int
maxInstances xs = maximum $ map (snd) $ countInstances xs

spaceOrStar :: [Int] -&gt; Int-&gt; Char
spaceOrStar xs y
  | y `elem` xs = '*'
  | otherwise = ' '

getData :: (Eq a, Ord a) =&gt; [a] -&gt; Int -&gt; [a]
getData xs n = [fst x | x &lt;- countInstances xs, snd x &gt;= n]

histRow :: [Int] -&gt; String
histRow xs = map (spaceOrStar xs) [0..9]

buildHist :: [Int] -&gt; String
buildHist xs = intercalate "\n" . map (histRow) $ map (getData xs) . reverse $ [1..maxInstances xs]

histogram :: [Int] -&gt; String
histogram xs = buildHist xs ++ "\n==========\n0123456789\n"

main :: IO()
main = do
    print $ skips "ABCD"
    print $ skips "hello!"
    print $ skips [1]
    print $ skips [True, False]
    print $ skips ([] :: [Int])
    print $ localMaxima [2,9,5,6,1]
    print $ localMaxima [2,3,4,1,5]
    print $ localMaxima [1,2,3,4,5]
    putStr $ histogram [1,4,5,4,6,6,3,4,2,4,9]
Overall it's pretty nice. I like that you've learned how to use $; it really improves readability in my opinion. One thing that immediately caught my attention though was the chain of prints. Haskell can do better! You can for example put those in a list, and mapM_ print over it.

let fns = 
    [ skips "ABCD"
    , skips "hello!"
    , skips [1]
    , skips [True, False]
    , skips ([] :: [Int])
    , localMaxima [2,9,5,6,1]
    , localMaxima [2,3,4,1,5]
    , localMaxima [1,2,3,4,5]
    ]

mapM_ print fns


This makes it easier to refactor  later, if you want to do something else than print, write them to different files, all kinds of stuff. The lisp-ish approach of code-as-data is quite useful when enumerating cases like you did here.



isMiddleMax (x:y:z:[]) = x &lt; y &amp;&amp; y &gt; z


Spooky! Use -Wall to get a proper warning for that one: incomplete patterns in function .... You might want to consider adding a meaningful error message:

isMiddleMax _ = error "Only works on 3-element lists!"




triples (x:xs)
  | length (x:xs) &lt; 3 = []


Dunno if you know, but there exists a thing called "alternate name capture"; it's written like so:

triples (x:xs @ allXs)
  | length allXs &lt; 3 = []


If you consider this an overkill, why not simply change the clause to length xs &lt; 2? I don't like the same pattern repeated for some reason.



countInstances xs = map (countInstance) $ groupSort xs

maxInstances xs = maximum $ map (snd) $ countInstances xs


The parens around countInstance and snd are unnecessary (it's not terrible to leave them, just pointing that out). HLint is a useful tool that can provide such hints for you!



Oh, and also, in the second case; multiple $ are a bit less readable; consider this instead:

maxInstances xs = maximum . map (snd) . countInstances $ xs


This expresses it in a truly functional way, and makes it susceptible to eta-reduction!:

maxInstances = maximum . map (snd) . countInstances


This is called point-free (not to be confused with pointless :)) notation, and makes it extremely clear that the function is indeed a composition of other functions ((.) is Haskell's composition operator).



I'll add more if I notice any more areas for improvement.