OK, here is the function in all of its point-free one line glory (or hideousness) with nary a lambda in sight...
module Main where
import Data.List ( sortBy, groupBy )
import Data.Function ( on )
import Data.Ord ( comparing )
import Control.Arrow ( (&&&) )
stageone :: String -> IO [[String]]
stageone = fmap (map (map snd) . groupBy ((==) `on` fst) . sortBy (comparing fst) . map (length &&& id) . lines) . readFile
Let's break it on down. The top level is a composition of readFile
and fmap
. We always read function composition from right to left. The point-free argument to stageone
is passed to readFile
, which returns an IO String
into which fmap
sends its function argument to transform the String
inside the IO
candy shell.
This function is another composition which we read right to left. First, we apply lines
to the String
to break it into a list of lines. We then map
(length &&& id)
over the list of lines, which transforms each line into a pair consisting of the length of the line and the line itself. Next, we sort the list of pairs by comparing the first element of each (the length). The sort is stable, so the lines will remain in their original order within a run of the same length lines. Next, we apply groupBy
with ((==) `on` fst)
, which groups runs of pairs with the same first element (the length) into their own sublists. Finally, we apply map (map snd)
over the list of lists of pairs. The outer map
iterates over the groups, and the inner map
iterates over the list of pairs within a group, replacing each pair with its second element (the line).
Phew!