I don’t know other guys, but for me, sometimes I am confused with $
(“application operator”) and .
(“function composition”) in Haskell
, so I want to write a small summary to differentiate them.
Check the type of these two operators:
> :t ($)
($) :: (a -> b) -> a -> b
> :t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
For infix operator, $
, its left operand should be a function whose type is a->b
, the right operand should be the input parameter of this function: a
, then get the result b
. Check following example:
countWords :: String -> Int
countWords s = length $ words s
words
returns a String
list which is the input argument of length
, then length
returns how many words this string has. Run countWords
in ghci
:
> let countWords s = length $ words s
> countWords "a b c"
3
See .
now; maybe another the type notation of .
can give you a hand in comprehending it:
(.) :: (b -> c) -> (a -> b) -> (a -> c)
We can think the operands of .
are both functions, and the return value is also a function. however, the three functions are not random, but have relationships with their parameters. Modify countWords
and run it in ghci
:
> let countWords s = length . words s
<interactive>:12:29: error:
* Couldn't match expected type `a -> [a0]'
with actual type `[String]'
* Possible cause: `words' is applied to too many arguments
In the second argument of `(.)', namely `words s'
In the expression: length . words s
In an equation for `countWords': countWords s = length . words s
* Relevant bindings include
countWords :: String -> a -> Int (bound at <interactive>:12:5)
Woops! Error is generated. The reason is the space ” “, or function application has the highest precedence, so words s
should be evaluated first:
:t words
words :: String -> [String]
The return value is [String]
, not a function, so it doesn’t satisfy the type of .
who requires the two operands must be functions. Change the countWords
definition:
> let countWords s = (length . words) s
> countWords "a b c"
3
This time it works like a charm! For the same reason, the second operand of $
must be consistent with the input parameter of the first operand:
> let countWords = length $ words
<interactive>:18:18: error:
* No instance for (Foldable ((->) String))
arising from a use of `length'
* In the expression: length $ words
In an equation for `countWords': countWords = length $ words
It is time to warp it up: the operands of .
is function, and we can use .
to chain many functions to generate a final one which works as the left operand of $
, feed it with one argument and produce the final result. Like this:
> length . words $ "1 2 3"
3
The same as:
> length $ words $ "1 2 3"
3