For the Standard ML function composition operator to have a well-defined type it is necessary for the source of the first function to be identical to the target of the second. For both functions, the other part of the type is not constrained. Recall the definition of the compose function which is equivalent to (op o).
val compose = fn (f, g) => fn x => f(g(x));
We can calculate the type of compose as follows. It is a function which takes a pair so we may say that it is of the form (¤ * ¤) -> ¤. We do not wish to restrict the argument f and thus we assign it a type 'a -> 'b since this is the worst possible type it can have. This forces g to be of the form ¤ -> 'a and we have (('a -> 'b) * (¤ -> 'a)) -> ¤ as our current type for compose. Of course, there is no reason to restrict the type of g either so we assign it the type 'c -> 'a and thus calculate (('a -> 'b) * ('c -> 'a)) -> ('c -> 'b) as the type of the compose function.
Define a function with type (('a -> 'a) * ('a -> 'a)) -> ('a -> 'a) without using a type constraint.
What is the type of curry? What is the type of uncurry?