As a first step we observe that your definition
outputList x y = concat . map ($ y) $ map ($ x) [getRow,getColumn,getBlock]
can be rewritten using the function composition operator (.)
instead of the function application operator ($)
as follows.
outputList x y = (concat . map ($ y) . map ($ x)) [getRow,getColumn,getBlock]
Next we notice that map
is another name for fmap
on lists and satisfies the fmap
laws, therefore, in particular, we have map (f . g) == map f . map g
. We apply this law to define a version using a single application of map
.
outputList x y = (concat . map (($ y) . ($ x))) [getRow,getColumn,getBlock]
As a final step we can replace the composition of concat
and map
by concatMap
.
outputList x y = concatMap (($ y) . ($ x)) [getRow,getColumn,getBlock]
Finally, in my opinion, although Haskell programmers tend to use many fancy operators, it is not a shame to define the function by
outputList x y = concatMap (\f -> f x y) [getRow,getColumn,getBlock]
as it clearly expresses, what the function does. However, using type class abstractions (as demonstrated in the other answer) can be a good thing as you might observe that your problem has a certain abstract structure and gain new insights.