Bladt
Bladt

Reputation: 305

Why isn't composing two functions working?

Apart from readability and loss of generality, what is wrong with the following definition:

maxPlusOne :: (Ord a, Num a) => a -> a -> a
maxPlusOne = (1+) . max

The compiler will complain that it can't deduce Num (a -> a) from +. But it seems to me what it needs is Num a => a -> a, which is precisely the type of (1+).

Here's the error:

<interactive>:5:52:
    Could not deduce (Num (a -> a)) arising from a use of ‘+’
    from the context (Ord a, Num a)

Upvotes: 2

Views: 106

Answers (2)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477881

I think you interpret the . too mathematically (as in f "after" g). The . is simply:

(.) :: (b -> c) -> (a -> b) -> a -> c

So its semantical meaning is that it takes one parameter a, then calls g on a, and then call f on g. You can define it as:

(.) f g x = f $ g x

Although Haskell extensions are proposed (like this one) that use the type system to derive how much "parameters" to transport, and some have written this in template Haskell, it is not a standard feature.

How can you solve it?

Simply query at least one parameter x in the head of your function:

maxPlusOne :: (Ord a, Num a) => a -> a -> a
maxPlusOne x = (1+) . max x

or specify it fully:

maxPlusOne :: (Ord a, Num a) => a -> a -> a
maxPlusOne x y = (1+) $ max x y

or use extensions on (.).

If you use jaspervdj's extension

maxPlusOne :: (Ord a, Num a) => a -> a -> a
maxPlusOne = (1+) $.$ max

Upvotes: -2

AJF
AJF

Reputation: 11924

Let's inspect the individual types:

max :: (Ord a) => a -> a -> a

Okay, this takes an a, and returns a function that takes an a and returns an a.

(1 +) :: (Num a) => a -> a

This is simple enough. It takes a number a and adds one to it.

If we recall that a . b is equivalent to \q -> a (b q) we get this from your definition:

(1 +) . max   ==   \q -> 1 + (max q)

Can you see the problem? You're passing a type a -> a to (1 +), because max is only getting one argument.


This is how to fix it, whilst keeping pointfree style.

maxPlusOne = (.) (1 +) . max

This expands to

maxPlusOne = \q -> (1 +) . max q

which expands to

maxPlusOne = \q1 -> \q2 -> 1 + (max q1 q2)

Which is your desired function.


You could also define another type of composition:

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

and then define

maxPlusOne = (+ 1) .: max

Upvotes: 9

Related Questions