Document the behavior of ScopedTypeVariables vis-à-vis instance heads more precisely
The User's Guide has a Lexically scoped type variables section that describes the intricacies of how ScopedTypeVariables works. There is a wonderfully detailed subsection, Declaration type signatures, that describes how ScopedTypeVariables works for things such as standalone type signatures. Another subsection, Class and instance declarations, is comparatively terse. This is a shame, because it doesn't really convey the subtleties involved in the way ScopedTypeVariables works in instance heads (at least, in my opinion). Here are some surprising observations that I would like to see mentioned in this subsection:
-
Unlike in type signatures, where type variables are only brought into scope when there is an outermost
forallwith no surrounding parentheses, the presence of parentheses does not matter for instance heads. For example, the following examples will typecheck:{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} module Foo where import Data.Proxy class C a where m :: Proxy a instance (C (Maybe a)) where m = Proxy @(Maybe a) instance (forall a. C [a]) where m = Proxy @[a]The second instance is particularly surprising, since
awould not be brought into scope of the body of a functionfooif it had the type signaturefoo :: (forall a. Dict (C [a])). This is definitely something that should be noted. -
Instance heads allow both implicitly and explicitly bound type variables to scope over the method bodies. For example, the following will typecheck:
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} module Foo where import Data.Proxy class C a where m :: Proxy a instance (forall a. C (Either a b)) where m = Proxy @(Either a b) -
While
ScopedTypeVariablescontrols the ability of type variables from the instance head to scope over method bodies, it does not control scoping in other places. For example, the following will work even in the absence ofScopedTypeVariables:{-# LANGUAGE ExplicitForAll #-} {-# LANGUAGE InstanceSigs #-} module Foo where import Data.Proxy class C a where m :: Proxy a instance forall a. C [a] where m :: Proxy [a] m = ProxyNotice that the
afrom the instance head scopes over the instance signature formeven thoughScopedTypeVariablesis not enabled. (If you tried mentioningain the body ofm, however, you would needScopedTypeVariables.)