Reputation: 1582
I'm aware that using the . operator chains functions together, like so:
isLessThanZero x
| x < 0 = True
| otherwise = False
(isLessThanZero . negate) 3 -- Produces True
Or with $:
getNegNumbers x = map (*x) [-1, -2, -3]
filter isLessThanZero $ getNegNumbers 2 -- Produces [-2, -4, -6]
But if I were to do something like:
(subtract . negate) 1 2 -- 3
negate $ subtract 1 2 -- -1
The results here are different, and it doesn't make sense because the two functions accept a different number of arguments. With .
the negate
function checks whether a number is negative or not, but two arguments are supplied. It could imply that the expression is left-associative.
negate (subtract 1 2) -- -1
(subtract . negate) 1 2 -- 3
But this is confusing because in the first example:
(isLessThanZero . negate) 3
The expression produces True
, which implies that the function negate
was executed first, then isLessThanZero
is called. But in the latest example, it appears that subtract
was called first and then negate
was called. So I'm not sure what's going on here. But this is what's even more confusing:
subtract 1 2 -- Produces 1!
Which implies that the entire expression:
(subtract . negate) 1 2 -- Produces 3!
is a side-effect of using function chaining.
My theory is this, decomposing thus:
We know that 2 - (-1) = 3. So, the expression is still right-associative.. I think. negate
is still called first like in the previous example, only that it affects the first argument, rather than both arguments, which makes sense because negate accepts only one argument, and we're not mapping the function at all.
So, when it comes to chaining functions with differing number of arguments, how should Haskell respond to that?
Upvotes: 4
Views: 211
Reputation: 27216
On Haskell, functions are curried, so:
f :: a -> b -> c
is the same as:
f :: a -> (b -> c)
So, when you're calculating f 1 2
, you're first applying a
to f
and get a function with type b -> c
. And then you apply b
and get c
.
Now, lets follow the types:
Prelude> :t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
Prelude> :t negate
negate :: Num a => a -> a
Prelude> :t subtract
subtract :: Num a => a -> a -> a
Prelude> :t (subtract.negate)
(subtract.negate) :: Num b => b -> b -> b
So, what is the type of (subtract.negate) 2
?
Prelude> :t (subtract.negate) 1
(subtract.negate) 1 :: Num b => b -> b
As you can see, negate
got an 1
and gave -1
, but subtract
took that -1
, and gave Int -> Int
. And then you applied 2
to that Int -> Int
and got 3
.
Shortly, (.)
is always right-associative and takes (b -> c)
and (a -> b)
. The only tricky part is, that c
itself can be (d -> e)
.
Upvotes: 4
Reputation: 85767
It could imply that the expression is left-associative.
I don't understand what you mean by "less-associative" here.
Function application is left associative, meaning a b c
parses as (a b) c
.
Composition is defined like this: (a . b) c = a (b c)
.
Thus
(subtract . negate) 1 2
= -- function application associates to the left
((subtract . negate) 1) 2
= -- definition of composition
(subtract (negate 1)) 2
= -- definition of subtract
2 - (negate 1)
= -- definition of negate
2 - (-1)
= -- definition of -
3
The other expression goes like this (with a $ b = a b
):
negate $ subtract 1 2
= -- function application has higher precedence than any operator
negate $ (subtract 1 2)
= -- definition of $
negate (subtract 1 2)
= -- definition of negate
-(subtract 1 2)
= -- definition of subtract
-1
Upvotes: 2
Reputation: 1755
Haskell approach is to consider all functions as taking one argument and returning one value, even for functions with multiple arguments.
Therefore, the subtract
function whose signature is:
subtract :: Num a => a -> a -> a
may also be seen:
subtract :: Num a => a -> (a -> a)
a function which takes a numerical argument and returns a function which takes one numerical value and returns a numerical value.
Considering (.)
, its signature is:
(.) :: (y -> z) -> (x -> y) -> x -> z
It takes two functions a returns a function.
If you apply it to (subtract . negate)
, you have this signature:
(subtract . negate) :: Num a => (a -> (a -> a)) -> (a -> a) -> (a -> (a -> a))
where:
x = a
y = a
z = a -> a
The signature this function is perfectly valid.
Note that subtract 1 2
is acting like 2 - 1
.
The (subtract . negate)
function is a function which takes one numerical value, negates it and returns a function which takes another numerical value from which the negated value will be subtracted.
Note also that negate (subtract 1 2)
is equal to -1
, not 3
!
Upvotes: 4