Function types bound to arguments

Brent Yorgi's excellent UPenn Haskell Course presents:

fmap2 :: Functor f => (a -> b -> c) -> (f a -> f b -> f c)
fmap2 h fa fb = undefined

      

Types h

, fa

and fb

break into:

h  :: a -> b -> c
fa :: f a
fb :: f b

      

It is not clear to me why this h

applies to the whole function (a -> b -> c)

.

Why h

can't link to a

and fa

to (b -> c)

?

Do the parentheses form in (a -> b -> c)

?

EDIT

Consider leftaroundabout comment :

For those who are just reading this without knowing which particular course it says: fmap2

cannot be determined with this signature.

In fact it should be liftA2 :: Applicative a => (a->b->c) -> (f a->f b->f c)

+3


source to share


1 answer


Yes, the parentheses mean exactly the way you say it. Since (->)

right-associative but not mathematically associative , the parentheses on the left side of a function arrow cannot be separated in the way you suggest:

(a -> b) -> (f a -> f b) /= a -> b -> f a -> f b

      

The operator ->

in this respect is exactly the same as the exponential operator ^

, which is conditionally right-associative, but not mathematically associative :

(2 ^ 2) ^ (2 ^ 2) /= 2 ^ 2 ^ 2 ^ 2
4       ^ 4       /= 2 ^ (2 ^ (2 ^ 2))
256               /= 2 ^ (2 ^ 4)
256               /= 2 ^ 16
256               /= 65536

      

(The exponentiation analogy is not my own invention, function types are "exponential types" in the same sense as (a, b)

"product type" but Either a b

"sum type". But note that it is a -> b

similar b ^ a

, not a ^ b

. See .this blog post for a heavy explanation example , and also this answer provides a mathematical overview of type algebra .)

The obvious oddity with fmap2

is that the type looks like it takes one parameter, but the definition looks like it takes three. Compare this version, which at least looks more like a type signature to me:

fmap2 :: Functor f => (a -> b -> c) -> (f a -> f b -> f c)
fmap2 h = \fa fb -> undefined

      



Now we have a good thing with one argument fmap2 h = ...

, with the right letter "two arguments". The trick is that in Haskell these two expressions are equivalent [*]: The Haskell Report speaks of the form of a "function" with parameters in LHS, is "semantically equivalent" for simple lambda template binding.

You can also rewrite the type to exclude the parentheses to the right of the arrow, because it ->

is right-associative:

   (a -> b -> c) -> (f a -> f b -> f c) 
== (a -> b -> c) -> f a -> f b -> f c

      

as

   (2 ^ 2 ^ 2) ^ (2 ^ 2 ^ 2)
== (2 ^ 2 ^ 2) ^ 2 ^ 2 ^ 2

      

[*]: they are semantically equivalent, but when compiled with GHC, their performance characteristics can and sometimes differ. Optimizer GHC handles f x = ...

and f = \x -> ...

in different ways.

+12


source







All Articles