Starting point:
fn :: [a] -> Int
fn = (2 *) . length
Let\'s say we only want to constrain the return value, then we could write:
If the type of your fn can be automatically inferred without a signature, and you merely wish the compiler to check whether the inferred type is of the right form, you might use something along the following.
The idea is to write something such as
fnSig :: exists _1 _2. forall a. _1 a -> _2
fnSig = fn
except that Haskell does not allow the existential types above. However, existential types can be emulated using higher-rank types as follows:
{-# LANGUAGE RankNTypes #-}
fnSig :: (forall _1 _2.
(forall a. _1 a -> _2) -- your actual type, _'s are the unknowns
->r)->r
fnSig = \k->k fn -- the compiler infers _1=[] , _2=Int
-- fn :: [] a -> Int
fn = (2 *) . length
The above "trick" is essentially the same as the one used in e.g. runST.
Alternatively, one could declare an ad-hoc existential data type.
{-# LANGUAGE GADTs #-}
data Ex where Ex :: (forall a. _1 a -> _2) -> Ex
fnSig = Ex fn
which should force the compiler to perform the same type checking.