Here's a function polymorphic in 3 types:
:t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
and here a non polymorphic function:
:t Data.Char.digitToInt
Data.Char.digitToInt :: Char -> Int
If we apply the former to the latter, we get a function polymorphic in 1 type:
:t (.) Data.Char.digitToInt
(.) Data.Char.digitToInt :: (a -> Char) -> a -> Int
which means that (.)
was "instantiated" (I'm not sure this is the correct term; as a C++ programmer, I'd call it so) with b === Char
and c === Int
, so the signature of the (.)
that gets applied to digitToInt
is the following
(Char -> Int) -> (a -> Char) -> a -> Int
My question is: is there a way to have this signature printed on screen, given (.)
, digitToInt
and the "information" that I want to apply the former to the latter?
For who's interested, this question was earlier closed as duplicate of this one.
Other answers require the help of functions that have been defined with artificially restricted types, such as the asTypeOf
function in the answer from HTNW. This is not necessary, as the following interaction shows:
Prelude> let asAppliedTo f x = const f (f x)
Prelude> :t head `asAppliedTo` "x"
head `asAppliedTo` "x" :: [Char] -> Char
Prelude> :t (.) `asAppliedTo` Data.Char.digitToInt
(.) `asAppliedTo` Data.Char.digitToInt
:: (Char -> Int) -> (a -> Char) -> a -> Int
This exploits the lack of polymorphism in the lambda-binding that is implicit in the definition of asAppliedTo
. Both occurrences of f
in its body must be given the same type, and that is the type of its result. The function const
used here also has its natural type a -> b -> a
:
const x y = x