Reputation: 1217
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
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
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