A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/exts/scoped_type_variables.html below:

Website Navigation


6.11.5. Lexically scoped type variables — Glasgow Haskell Compiler 9.12.2 User's Guide

6.11.5. Lexically scoped type variables¶
ScopedTypeVariables¶
Implies:

ExplicitForAll

Since:

6.8.1

Status:

Included in GHC2024, GHC2021

Enable lexical scoping of type variables explicitly introduced with forall.

GHC supports lexically scoped type variables, without which some type signatures are simply impossible to write. For example:

f :: forall a. [a] -> [a]
f xs = ys ++ ys
     where
       ys :: [a]
       ys = reverse xs

The type signature for f brings the type variable a into scope, because of the explicit forall (Declaration type signatures). The type variables bound by a forall scope over the entire definition of the accompanying value declaration. In this example, the type variable a scopes over the whole definition of f, including over the type signature for ys. In Haskell 98 it is not possible to declare a type for ys; a major benefit of scoped type variables is that it becomes possible to do so.

An equivalent form for that example, avoiding explicit forall uses Pattern type signatures:

f :: [a] -> [a]
f (xs :: [aa]) = xs ++ ys
  where
    ys :: [aa]
    ys = reverse xs

Unlike the forall form, type variable a from f’s signature is not scoped over f’s equation(s). Type variable aa bound by the pattern signature is scoped over the right-hand side of f’s equation. (Therefore there is no need to use a distinct type variable; using a would be equivalent.)

6.11.5.1. Overview¶

The design follows the following principles

A lexically scoped type variable can be bound by:

In Haskell, a programmer-written type signature is implicitly quantified over its free type variables (Section 4.1.2 of the Haskell Report). Lexically scoped type variables affect this implicit quantification rules as follows: any type variable that is in scope is not universally quantified. For example, if type variable a is in scope, then

(e :: a -> a)     means     (e :: a -> a)
(e :: b -> b)     means     (e :: forall b. b->b)
(e :: a -> b)     means     (e :: forall b. a->b)
6.11.5.2. Declaration type signatures¶

A declaration type signature that has explicit quantification (using forall) brings into scope the explicitly-quantified type variables, in the definition of the named function. For example:

f :: forall a. [a] -> [a]
f (x:xs) = xs ++ [ x :: a ]

The “forall a” brings “a” into scope in the definition of “f”.

This only happens if:

6.11.5.3. Expression type signatures¶

An expression type signature that has explicit quantification (using forall) brings into scope the explicitly-quantified type variables, in the annotated expression. For example:

f = runST ( (op >>= \(x :: STRef s Int) -> g x) :: forall s. ST s Bool )

Here, the type signature forall s. ST s Bool brings the type variable s into scope, in the annotated expression (op >>= \(x :: STRef s Int) -> g x).

6.11.5.4. Pattern type signatures¶

A type signature may occur in any pattern; this is a pattern type signature. For example:

-- f and g assume that 'a' is already in scope
f = \(x::Int, y::a) -> x

g (x::a) = x

h ((x,y) :: (Int,Bool)) = (y,x)

In the case where all the type variables in the pattern type signature are already in scope (i.e. bound by the enclosing context), matters are simple: the signature simply constrains the type of the pattern in the obvious way.

Unlike expression and declaration type signatures, pattern type signatures are not implicitly generalised. The pattern in a pattern binding may only mention type variables that are already in scope. For example:

f :: forall a. [a] -> (Int, [a])
f xs = (n, zs)
  where
    (ys::[a], n) = (reverse xs, length xs) -- OK
    (zs::[a])    = xs ++ ys                     -- OK

    Just (v::b)  = ...  -- Not OK; b is not in scope

Here, the pattern signatures for ys and zs are fine, but the one for v is not because b is not in scope.

However, in all patterns other than pattern bindings, a pattern type signature may mention a type variable that is not in scope; in this case, the signature brings that type variable into scope. For example:

-- same f and g as above, now assuming that 'a' is not already in scope
f = \(x::Int, y::a) -> x           -- 'a' is in scope on RHS of ->

g (x::a) = x :: a

hh (Just (v :: b)) = v :: b

The pattern type signature makes the type variable available on the right-hand side of the equation.

Bringing type variables into scope is particularly important for existential data constructors. For example:

data T = forall a. MkT [a]

k :: T -> T
k (MkT [t::a]) =
    MkT t3
  where
    (t3::[a]) = [t,t,t]

Here, the pattern type signature [t::a] mentions a lexical type variable that is not already in scope. Indeed, it must not already be in scope, because it is bound by the pattern match. The effect is to bring it into scope, standing for the existentially-bound type variable.

It does seem odd that the existentially-bound type variable must not be already in scope. Contrast that usually name-bindings merely shadow (make a ‘hole’) in a same-named outer variable’s scope. But we must have some way to bring such type variables into scope, else we could not name existentially-bound type variables in subsequent type signatures.

Compare the two (identical) definitions for examples f, g; they are both legal whether or not a is already in scope. They differ in that if a is already in scope, the signature constrains the pattern, rather than the pattern binding the variable.

6.11.5.5. Class and instance declarations¶

ScopedTypeVariables allow the type variables bound by the top of a class or instance declaration to scope over the methods defined in the where part. Unlike Declaration type signatures, type variables from class and instance declarations can be lexically scoped without an explicit forall (although you are allowed an explicit forall in an instance declaration; see Explicit universal quantification (forall)). For example:

class C a where
  op :: [a] -> a

  op xs = let ys::[a]
              ys = reverse xs
          in
          head ys

instance C b => C [b] where
  op xs = reverse (head (xs :: [[b]]))

-- Alternatively, one could write the instance above as:
instance forall b. C b => C [b] where
  op xs = reverse (head (xs :: [[b]]))

While ScopedTypeVariables is required for type variables from the top of a class or instance declaration to scope over the /bodies/ of the methods, it is not required for the type variables to scope over the /type signatures/ of the methods. For example, the following will be accepted without explicitly enabling ScopedTypeVariables:

class D a where
  m :: a -> a

instance Num a => D [a] where
  m :: [a] -> [a]
  m x = map (*2) x

Note that writing m :: [a] -> [a] requires the use of the InstanceSigs extension.

Similarly, ScopedTypeVariables is not required for type variables from the top of the class or instance declaration to scope over associated type families, which only requires the TypeFamilies extension. For instance, the following will be accepted without explicitly enabling ScopedTypeVariables:

class E a where
  type T a

instance E [a] where
  type T [a] = a

See Scoping of class parameters for further information.


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4