
On 11/25/2012 04:43 AM, Daniel Fischer wrote:
On Sonntag, 25. November 2012, 02:27:31, Christopher Howard wrote:
More like parentheses in algebra, they serve to group together parts of a (type) expression that would be grouped differently by the precedence and/or associativity of operators, e.g. in
foo :: (Int -> a) -> a foo f = f 42
The function arrow associates to the right, so without them, the signature would become Int -> (a -> a) which is an entirely different type.
Or
bar :: Either a (b -> a) -> b -> a
where it's precedence. Prefix type application binds strongest (like function application at the value level), so we can't omit the parentheses, otherwise we'd get
(Either a b) -> a -> b -> a
Let's be more verbose and call it
(.) :: (intermediate -> final) -> (original -> intermediate) -> original -> final
The section (. sqr) is equivalent to \f -> f . sqr, so sqr is the second argument of (.) and we must unify its type with the type of the second argument of (.), which is (original -> intermediate).
So original = Double, intermediate = Double, and here (.) is used at the more restricted type
(.) :: (Double -> final) -> (Double -> Double) -> Double -> final
the second argument is supplied, hence its type is removed, leaving
(. sqr) :: (Double -> final) -> Double -> final
Yup, modulo renaming, we have exactly that.
Now, (. sqr) is used as the first argument of (.). So we must unify its type with (intermediate -> final), the type of (.)'s first argument.
Now, the function arrow is right-associative, so we can also write
(. sqr) :: (Double -> c) -> (Double -> c)
and that makes it clear that
intermediate = (Double -> c) final = (Double -> c)
So the outer (.) is used at type
(.) :: ((Double -> c) -> (Double -> c)) -> (original -> (Double -> c)) -> original -> (Double -> c)
The first argument is supplied, so
((. sqr) .) :: (original -> (Double -> c)) -> original -> (Double -> c)
Yup, modulo renaming and parentheses that can be omitted because -> is right- associative, that is exactly the same.
It's just easier to see what corresponds to what with the parentheses.
The pretty-printer of type signatures in ghci omits unnecessary parentheses. Read it (a -> (Double -> c)).
While for small types like here, unnecessary parentheses can increase the readability, for larger types, they usually decrease readability much more (ever looked at Lisp code?), because all is drowned in parentheses.
Beautiful explanation. The clouds are starting to clear. In particular, previously I did not understand that 1) parentheses are in all types but are omitted for readability when possible, and 2) when an argument is supplied to a function it unifies the supplied parameter type with the argument type. Knowing this makes a huge difference in looking at these transformations. -- frigidcode.com