maestron
maestron

Reputation: 11

Haskell, "Couldn't match expected type" in function composition

I'm trying to write a simple function to check whether one integer divides another by taking the modulus and checking if it's 0. My thought was something like

divides :: (Integral a) => a -> a -> Bool
divides = (==0) . (flip mod)

where divides a b would be true iff a divides b. However, this code gives me the error

Couldn't match expected type `a -> Bool' with actual type `Bool'
Expected type: b0 -> a -> Bool
 Actual type: b0 -> Bool
In the first argument of `(.)', namely `(== 0)'
In the expression: (== 0) . mod

I really don't see why this code doesn't work. Please enlighten me!

Upvotes: 1

Views: 651

Answers (3)

daniel gratzer
daniel gratzer

Reputation: 53881

The gist is, . will only feed each function 1 argument, but flip mod needs two. A simple solution is

(.:) = (.) . (.) -- the owl or boobs operator

divides = (0==) .: flip mod

where .: is

 (c -> d) -> (a -> b -> c) -> a -> b -> d

Upvotes: 6

Tarmil
Tarmil

Reputation: 11362

It looks like you're expecting the . operator to carry over the fact that flip mod takes two arguments to its result, ie. you expect this:

f :: a -> b -> c
g :: c -> d
g . f :: a -> b -> d

Unfortunately, it doesn't work that way. Functions always take a single argument, and multiple argument functions are "simulated" by having functions that return a function. For ex, a -> b -> c can be read as a -> (b -> c). Therefore what happens is as follows:

f :: a -> (b -> c)
g :: (b -> c) -> d
g . f :: a -> d

Thus why g (which is (== 0) in your case) expects a function, and the type of your function actually is a -> Bool and not a -> a -> Bool.

Upvotes: 2

bennofs
bennofs

Reputation: 11963

To see what's wrong with your code, just eta-expand it manually:

divides :: (Integral a) => a -> a -> Bool
divides = (==0) . (flip mod)
-- divides x = ((==0) . (flip mod)) x
--           = ((==0) $ flip mod x)
--           = flip mod x == 0

That last line doesn't typecheck, because (==) :: a -> a -> Bool, but divides x should be of type a -> Bool, not Bool!

The easiest way to correct the code would be to write it in a more expanded form, for example:

divides :: (Integral a) => a -> a -> Bool
divides x = (==0) . (`mod` x)

If you really want to write it eta-reduced, here is what it would look like:

divides :: (Integral a) => a -> a -> Bool
divides = ((==0).) . (flip mod)
-- divides x = ((==0).) $ (`mod` x)
--           = (==0) . (`mod x`)  

Upvotes: 4

Related Questions