[Haskell-beginners] types, parentheses, application, composition

Christopher Howard christopher.howard at frigidcode.com
Mon Nov 26 09:17:35 CET 2012


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

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 551 bytes
Desc: OpenPGP digital signature
URL: <http://www.haskell.org/pipermail/beginners/attachments/20121125/60532d02/attachment.pgp>


More information about the Beginners mailing list