The main issue in your code (and I think also what you were trying to solve) is that you're trying to collapse a list of functions by combining them with (-) :: Num a => a -> a -> a
, which doesn't really make sense. If I understand correctly, what you want is to apply a single value (the input list) to every function in a list. There are multiple ways to do this, as explained in the answers to this recent SO question. With this simple trick, you're almost there. Here's the rest of the algorithm.
For brevity's sake, I will use the following input list:
$> let list = [1,2,3,4,5]
Using one of the methods from the linked SO question, you can feed this to every function in the list:
$> sequenceA [maximum, minimum, sum] list
$> [5,1,15]
Then you can pattern match on the list to get the values you want:
$> let [ma, mi, su] = sequenceA [maximum, minimum, sum] list in [su - ma, su - mi]
$> [10,14]
One inconvenient of this let-in
construct is that it doesn't allow for eta-reduction of the list
variable, so you will either have to use a lambda or a where
clause. For a function this long, I think a lambda isn't very readable, so I prefer a where
clause:
main = interact $ unwords . map show . f . map read . words
where f xs = let [ma, mi, su] = sequenceA [maximum, minimum, sum] xs
in [su - ma, su - mi]
I modified your main
function a bit, since what you want to print out is not the list show list
, but every number in the list, separated by spaces: unwords $ map show list
. You can also think of this as the opposite of what you had done to get the list in the first place (map read . words
).
Because this involves pattern-matching, I think it works better as a multi-line function, but there might be ways to solve it as a one-liner (other than making f
a long and ugly lambda).
EDIT
Here is a one-liner which uses arrows (from Control.Arrow) and the Applicative instance of ->
to feed the list to steps 4 and 5, and therefore avoid the long lambda:
main = interact $ unwords . map show . (\(a,b) -> [a,b]) .
((-) . sum <*> maximum &&& (-) . sum <*> minimum) .
map read . words
There is still some pattern-matching involved (to convert the tuple produced by &&&
into a list), and I don't think it's more readable. Maybe there's a better way, but I don't really see it.