newbie
newbie

Reputation: 1217

What is the difference between a space between two functions and using the composition operator?

I was trying to define a function that determines if a number is a multiple of 3, I first did the easy case

isMultipleOfThree num = (==0) (rem num 3)

but actually even in that simple case, my first guess was to write my solution like

isMultipleOfThree num = (==0).(rem num 3)

which returns the error

Non type-variable argument in the constraint: Integral (a -> b) (Use FlexibleContexts to permit this) When checking that ‘isMultipleOfThree’ has the inferred type isMultipleOfThree :: forall b a. (Eq b, Integral (a -> b), Num b) => (a -> b) -> a -> Bool

and then tried to make it more "polymorphic":

The correct answer was as follows, but I thought that I was supposed to use a space instead of the composition operator... and then I just "guessed" I would try with the composition operator:

esMultiploDeTres = (==0).(flip (rem) 3)

So I guess that I would add to my original question: "what is a space between two functions?"

Upvotes: 1

Views: 206

Answers (2)

Silvio Mayolo
Silvio Mayolo

Reputation: 70267

isMultipleOfThree num = (==0) (rem num 3)

The (== 0) is called an operator section. It's equivalent to \x -> x == 0, hence this is the same as

isMultipleOfThree num = (\x -> x == 0) (rem num 3)

And we immediately apply the lambda, so we get

isMultipleOfThree num = rem num 3 == 0

Now, on the other hand, your second example is

isMultipleOfThree num = (\x -> x == 0) . (rem num 3)

Now we're not applying the lambda immediately. We're applying the (.) function, which is defined something like

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

Hence, we have

isMultipleOfThree num = (\y -> (\x -> x == 0) ((rem num 3) y))

Now the lambda on x is getting applied to an argument, so we can simplify the inner one

isMultipleOfThree num = (\y -> (rem num 3) y == 0)

And the parentheses around rem are extraneous, hence

isMultipleOfThree num = (\y -> rem num 3 y == 0)

So we're applying rem, a function of two arguments, to three arguments. Hence your error.

If you want to use (.) to define your function, we can. But the right-hand side needs to have an argument left out.

isMultipleOfThree = (== 0) . (\x -> rem x 3)

or

isMultipleOfThree = (== 0) . (`rem` 3)

Note that we don't mention your function argument num anymore. The composition on the right-hand side is expecting an argument. It's going to do (rem 3)on that argument and then compare the result against zero.(.)works on functions, and both sides of the(.)` operator should have an argument missing.

Note that what you might have been thinking of is ($), which is defined roughly as

($) :: (a -> b) -> (a -> b)
f $ x = f x

This really is just function application. It's not composition. We could easily write your original function as

isMultipleOfThree num = (== 0) $ (rem num 3)

The ($) actually does behave like juxtaposition. The only point of it is to change operator precedence. We can omit parentheses in this case

isMultipleOfThree num = (== 0) $ rem num 3

and this will still work, whereas if we omit parentheses in your original function

isMultipleOfThree num = (==0) rem num 3 -- Wrong!

Now we're trying to apply the (==0) function to three arguments, which is definitely not correct.

Upvotes: 3

chepner
chepner

Reputation: 531055

(.) is defined simply as

# Apply g to x, then apply f to the result.
f . g = \x -> f (g x)

which means

(f . g) x == f (g x)

Juxtaposition represents function application, while the . represents function composition.

Higher-order functions, such as (.), can take other functions as arguments, which means you can apply such a function to another function. f . g is an infix expression, equivalent to the prefix expression (.) f g, where (.) is applied to f, and that result is applied to g.

Upvotes: 4

Related Questions