So i\'m not too sure how to phrase this properly, but say I wanted to get the sum of all odd numbers in a list, do I have two functions (sumList and getOddNumbers) and combi
or is there a way to put these two together in a single function ...
sumOddList
?
Yes there is.
Chaining functions by using one's output as the other's input works, under lazy evaluation especially, but leaves us reliant on the fusion to be performed by a compiler. Which after all is not guaranteed to happen (and often doesn't).
Instead, what you said :
mapping f cons x xs = cons (f x) xs
filtering p cons x xs = if (p x) then (cons x xs) else xs
transduce xf cons z xs = foldr (xf cons) z xs
sumOddList xs = transduce (filtering odd) (+) 0 xs
Thus,
> sumOddList [1..10]
25
> sum [1,3..10]
25
> transduce (mapping (+1) . filtering odd) (+) 0 [1..10]
35
> sum . filter odd . map (+1) $ [1..10]
35
> sum . map (+1) . filter odd $ [1..10]
30
> transduce (filtering odd . mapping (+1)) (+) 0 [1..10]
30
This works because folds fuse by composing their reducer functions' transformers (like the mapping
and the filtering
above which are transforming their reducer argument cons
):
foldr (+) 0
. foldr (\x r -> x+1 : r) []
. foldr (\x r -> if odd x then x : r else r) []
$ [1..10]
=
foldr (+) 0
. foldr ((\cons x r -> cons (x+1) r) (:)) []
. foldr ((\cons x r -> if odd x then cons x r else r) (:)) []
$ [1..10]
=
foldr ((\cons x r -> cons (x+1) r) (+)) 0
. foldr ((\cons x r -> if odd x then cons x r else r) (:)) []
$ [1..10]
=
foldr ((\cons x r -> if odd x then cons x r else r)
((\cons x r -> cons (x+1) r) (+))) 0
$ [1..10]
=
foldr ( ( (\cons x r -> if odd x then cons x r else r)
. (\cons x r -> cons (x+1) r) ) (+)) 0
$ [1..10]
=
foldr ( (filtering odd . mapping (+1)) (+)) 0
$ [1..10]
=
foldr ( filtering odd ( mapping (+1) (+))) 0
$ [1..10]
=
30
One foldr
doing the work of the three. Fusion explicitly achieved, by composing the reducer functions after the cons
operation has been abstracted from them, each such changed function becoming a cons
transformer, thus, liable to be composed with other such cons
-transforming functions.
Well what you basically want to do is use the output of the getOddNumbers
as input for the sumList
function, so we can define a sumOddList
function as:
sumOddList :: [Integer] -> Integer
sumOddList l = sumList (getOddNumbers l)
Here l
is the list we want to process, and the result is thus a function application on the result of getOddNumbers l
(with sumList
the function).
(.)
functionThe above pattern is quite common: frequently we want to pass data first through a function g
, and the result through a function f
. Haskell has the (.) :: (b -> c) -> (a -> b) -> a -> c function to "chain" functions. We can thus chain sumList
and getOddNumbers
together like:
sumOddList :: [Integer] -> Integer
sumOddList = (.) sumList getOddNumbers
Notice that we no longer use an l
parameter here. sumOddList
is here defined as a "pipeline" where data is first passed to the getOddNumbers
, and then is "post-processed" by the sumList
function.
The (.)
function can also be used as an infix operator:
sumOddList :: [Integer] -> Integer
sumOddList = sumList . getOddNumbers
In the following are three equivalent ways to write the function oddSum :: [Integer] -> Integer
:
oddSum xs = sumList (getOddNumbers xs)
oddSum xs = sumList $ getOddNumbers xs
oddSum = sumList . getOddNumbers
Btw, have a look at the filter
and sum
functions in the Prelude with which you could replace getOddNumbers
and sumList
respectively.