
Chaddaï Fouché wrote:
- Why are top-level variables and function arguments treated differently by the type system?
They aren't
In a sense, they are. id :: (forall a. a -> a) useId :: (forall a. a -> a) -> (Int,Bool) brokenUseId :: (forall a. (a -> a) -> (Int,Bool)) brokenUseId :: (a -> a) -> (Int,Bool) Note that polymorphic variables in function argument types scope over the function results too by default. And that's a good thing. Otherwise, id :: a -> a would be understood as brokenId :: (forall a. a) -> (forall a. a) which is not at all intended ("id" specialized to _|_ values only). Basically, you only want higher-rank types in Haskell when you know what you're doing: due to parametric polymorphism it is less often useful to apply an argument function to, e.g., both Int and Bool than you might find in Python, for example. In Haskell, more often you would just take two functions as arguments e.g. useFunctions :: (Int -> Int) -> (Bool -> Bool) -> (Int,Bool) or more interestingly, let's make the caller be able to choose any kind of number: useFunctions2 :: (Num a) => (a -> a) -> (Bool -> Bool) -> (a,Bool) a.k.a. useFunctions2 :: forall a. (Num a) => (a -> a) -> (Bool -> Bool) -> (a,Bool) -Isaac