1

I'm trying to do a function that given an operator and a function will go through a list like such:

reduce (*) even [1,2,3,4,5] => 8
reduce (+) odd [1,2,3,4,5]  => 9

At first I had something like this:

reduce :: (Int-> Int-> Int) -> (Int->Bool) -> [Int] -> Int
reduce op x y = foldl op 1 (filter x y)

and it worked fine for multiplying, but added one number when trying to do a sum. So I thought of adding an if:

reduce :: (Int-> Int-> Int) -> (Int->Bool) -> [Int] -> Int
reduce op x y = 
    if op == (*) then foldl op 1 (filter x y)
        else if op == (+) then foldl op 0 (filter x y)
            else 0

However when I do this I get the following error:

Instance of Eq (Int -> Int -> Int) required for definition of reduce

What does it mean exactly? Any ideas? Thanks in advance

StarCharm
  • 51
  • 1
  • 4
  • For you to ponder: should `(\x y -> x + y + 0)` be equal to `(\x y -> x + y)`? If so, by what algorithm do you propose to determine this? If not, are you really comfortable with a refactoring that changes `reduce (\x y -> x + y)` to `reduce (\x y -> x + y + 0)` -- an apparently harmless local rewrite -- changing the behavior in such a drastic way? (I'm not saying these questions are unanswerable, but thinking carefully about how to answer them may be enlightening.) – Daniel Wagner Oct 12 '16 at 05:03
  • Possibly related question: [How to compare two functions for equivalence?](http://stackoverflow.com/q/17045941/791604) – Daniel Wagner Oct 12 '16 at 05:07

2 Answers2

4

It means that with your current imports you cannot compare functions for equality (and even with suitable imports, there is no really good way to do the equality you ask for).

One simple solution is to demand that the user provide both the operation and the unit, thus:

reduce op u x y = foldl op u (filter x y)

In many cases of interest, there is already a Monoid instance that specifies the operator and unit. For example, there are the Sum and Product instances, so you might consider writing

reduce inject p = foldMap inject . filter p

which would be used like this:

> reduce Sum even [1..5]
Sum {getSum = 6}
> reduce Product odd [1..5]
Product {getProduct = 15}
Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
3

What does it mean exactly? Any ideas? Thanks in advance

You're trying to compare some (Int -> Int -> Int) to another one. But functions cannot get checked on equality. Provide the initial value instead:

reduce :: (Int -> Int -> Int) -> (Int -> Bool) -> Int -> [Int] -> Int
reduce op f x xs = foldl op x (filter f xs)

By the way, you can generalize your function's type:

reduce :: (b -> a -> b) -> (a -> Bool) -> b -> [a] -> a
reduce op f x xs = foldl op x (filter f xs)

Alternatively, use a monoid and foldMap.

Zeta
  • 103,620
  • 13
  • 194
  • 236