types, parentheses, application, composition

Could someone explain more precisely to me what is the significance of parentheses in the type of an expression? Are they just some kind of syntactic sugar, that simplifies into a type without them, or are they more like parentheses in algebra, or...? In particular, I'm having difficulty understanding the results of composition or application that involves parentheses... I think I've got it figured out and then I find another strange expression that doesn't have the type I expect. Here is an example using the (self-made) function "sqr": code: --------
:t sqr sqr :: Double -> Double :t (.) (.) :: (b -> c) -> (a -> b) -> a -> c :t (. sqr) (. sqr) :: (Double -> c) -> Double -> c :t ((. sqr) .) ((. sqr) .) :: (a -> Double -> c) -> a -> Double -> c
Everything makes sense up until the last expression, "((. sqr) .)". Where did the "(a -> Double -> c)" come from? What's going on here? -- frigidcode.com

On Sonntag, 25. November 2012, 02:27:31, Christopher Howard wrote:
Could someone explain more precisely to me what is the significance of parentheses in the type of an expression? Are they just some kind of syntactic sugar, that simplifies into a type without them, or are they more like parentheses in algebra, or...?
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
In particular, I'm having difficulty understanding the results of composition or application that involves parentheses... I think I've got it figured out and then I find another strange expression that doesn't have the type I expect. Here is an example using the (self-made) function "sqr":
code: --------
:t sqr
sqr :: Double -> Double
:t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
Let's be more verbose and call it (.) :: (intermediate -> final) -> (original -> intermediate) -> original -> final
:t (. sqr)
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
(. sqr) :: (Double -> c) -> Double -> c
Yup, modulo renaming, we have exactly that.
:t ((. sqr) .)
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)
((. sqr) .) :: (a -> Double -> c) -> a -> 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.
--------
Everything makes sense up until the last expression, "((. sqr) .)". Where did the "(a -> Double -> c)" come from? What's going on here?
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.

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

On Mon, Nov 26, 2012 at 3:17 PM, Christopher Howard < christopher.howard@frigidcode.com> wrote:
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.
(1) parens are everywhere, some of them you don't see because of default associativity (2) type-inferencing unification is utterly opaque and mysterious -- Kim-Ee
-- frigidcode.com
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

Whoops, sorry for the noise. Those bullet points were the outline of some
notes I'm putting together.
-- Kim-Ee
On Mon, Nov 26, 2012 at 3:33 PM, Kim-Ee Yeoh
On Mon, Nov 26, 2012 at 3:17 PM, Christopher Howard < christopher.howard@frigidcode.com> wrote:
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.
(1) parens are everywhere, some of them you don't see because of default associativity (2) type-inferencing unification is utterly opaque and mysterious
-- Kim-Ee
-- frigidcode.com
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

On Sun, Nov 25, 2012 at 6:27 AM, Christopher Howard < christopher.howard@frigidcode.com> wrote:
Could someone explain more precisely to me what is the significance of parentheses in the type of an expression? Are they just some kind of
One thing you're missing is that parentheses often have multiple meanings. Specifically, the thing that's tripping you up is a section: a partially applied operator. (+) is an operator, with parentheses around it to turn it into a function. You can sometimes see this passed to e.g. fmap. By extension, (5 +) is a section: the operator (+) with its left parameter applied already, equivalent to \x -> 5 + x. (+ 5) has applied the right parameter instead of the left. If you have a parenthesized thing that starts or ends with an operator, it's a section. Your example "> :t ((. sqr) .)" has two of them, both partially applying (.). -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix/linux, openafs, kerberos, infrastructure http://sinenomine.net
participants (4)
-
Brandon Allbery
-
Christopher Howard
-
Daniel Fischer
-
Kim-Ee Yeoh