How to use foldr with guards? (I'm a Haskell learner)

I tried to implement this challenge from Hackerrank (No Idea! | HackerRank) in Haskell where you add 1 to your count if the number is in set A and subtract -1 if the number is in set B.

import qualified Data.Set as Set

setA :: Set.Set Int
setA = Set.fromList [3,1]

setB :: Set.Set Int
setB = Set.fromList [5,7]

numbers :: List
numbers = [1,5,3]

happinessF :: [Int] -> Set.Set Int -> Set.Set Int -> Int 
happinessF set1 set2 = foldr ((+) . cond) 0
  where cond x
            | Set.member x set1 = 1
            | Set.member x set2 = -1
            | otherwise = 0

When I run this code, I get an error saying:

Couldn’t match expected type: Set.Set Int with actual type: [Int]

Any ideas why this code isn’t working?

1 Like

Hey,

foldr with guards is probably fine. :slight_smile:

The error message says, “Couldn’t match expected type: Set.Set Int with actual type: [Int]”. That means exactly what it says: The compiler expects a Set of Ints, but it actually got a list of Ints.

If you think about why that happened, maybe you can see what else might be wrong about your code.

Hint: one way you can fix the problem is by changing the type signature for happinessF.

2 Likes

Also, do look at the full error message which includes detailed location information:

T2.hs:15:28: error:
    • Couldn't match expected type: Set.Set Int
                  with actual type: [Int]
    • In the second argument of ‘Set.member’, namely ‘set1’
      In the expression: Set.member x set1
      In a stmt of a pattern guard for
                     an equation for ‘cond’:
        Set.member x set1
   |
15 |             | Set.member x set1 = 1
   |                            ^^^^

That should help you locate the problem.

By the way, one hint for interpreting this error message is that “expected type” means the type the compiler expected at the location it points to, in this case:

Spoiler 1

set1 as argument to the Set.member function.

And “actual type” is a constraint from another place in the program, in this case:

Spoiler 2

The type signature of happinessF:

happinessF :: [Int] -> Set.Set Int -> Set.Set Int -> Int 
happinessF set1 set2
2 Likes

Thanks for your help! :grinning:I’ve managed to get the code running. I simply needed to explicitly add the list to the code for some reason.

happinessF :: [Int] → Set.Set Int → Set.Set Int → Int
happinessF (x:xs) set1 set2 = foldr ((+) . cond) 0 (x:xs)
where cond x
| Set.member x set1 = 1
| Set.member x set2 = -1
| otherwise = 0

2 Likes

Great to see you’ve figured it out!

Note that you don’t have to write (x:xs) for every list. You can (and should) just write:

happinessF xs set1 set2 = foldr ((+) . cond) 0 xs

Also because with (x:xs) it will throw an error if you call it with the empty list as argument, because it requires the input list to have at least one element.

2 Likes

The reason behind this is that you can leave arguments out, but only the last argument on both sides of the equality (the = sign). For example if you have:

f x y = g x y

Then you can write:

f x = g x

Or even:

f = g

But if the order of the arguments is different, as in:

f x y = g y x

Then you can’t leave out that x argument, because it is not the last argument on the left side of the equality.

So in your case you can only leave of the xs argument if you reorder your arguments:

happinessF :: Set.Set Int -> Set.Set Int -> [Int] -> Int
happinessF set1 set2 xs = foldr ((+) . cond) 0 xs
  where ...

Then you can leave out the argument:

happinessF :: Set.Set Int -> Set.Set Int -> [Int] -> Int
happinessF set1 set2 = foldr ((+) . cond) 0
  where ...
2 Likes

Thanks a lot! Your explanations have been very helpful :star_struck:

2 Likes