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?
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
!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With