haskelltype-signature

Understanding Haskell Type Signature


I have to convert a Haskell type signature into a term. The type signature is :

f :: (a -> b -> c) -> (d -> b) -> (d -> a) -> d -> c

The correct resulting term is :

f g h j x = g (j x) (h x)

and here lies my problem as I understand it g is a function which returns a function which returns c and c is function which returns a function d which returns b and b is a function which returns itself which then returns itself again which then returns c.

Correct me if i am wrong.

What I don't get is why is g taking (j x) as first argument and (h x) as second argument. Shouldn't it be the other way around? Haskell is right associative and h is the secound parameter given to the function f and not j.


Solution

  • g :: a -> b -> c, h :: d -> b, j :: d -> a, and x :: d are all independent arguments to f; their order implies nothing about how we might end up using them in the definition of f.

    To start, we know that f uses its arguments to return a value of type c. But none of the arguments have a value of type c; the only way to get a value of type c is to use g. But in order to use g, you need arguments of type a and type b, and none of f's arguments have those types. But we could use h and j to get them, if we had an argument of type d to apply them to, and lo and behold, we do have a value of type d: the argument x!.

    f g h j x = let aValue = j x
                    bValue = h x
                    cValue = g aValue bValue
                in cValue
    

    which can be flattened to the original answer of

    f g h j x = g (j x) (h x)
    

    If you want to think of the return value of f as being d -> c, rather than just c, you can eliminate x from the definition with some point-free trickery.

    f g h j = g <$> j <*> h  -- liftA2 g j h
    

    You can even go a little further to remove h and j as arguments, but the result, though simple, is even more incomprehensible:

    f = flip . liftA2
    

    Moral of the story: sometimes point-free style abstracts away distracting details, other times it completely obscures the meaning of the function.