-
Notifications
You must be signed in to change notification settings - Fork 71
Add documentation for GHC-45510 #505
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
--- | ||
title: Term variables cannot be used here | ||
summary: A variable defined at the level of terms cannot be directly used in type or kind signatures | ||
severity: error | ||
introduced: 9.10.1 | ||
--- | ||
|
||
Through using [`RequiredTypeArguments`], Haskell gets support for programming with Dependent Types. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not true. idInvis :: forall a. a -> a
idInvis @t x = x :: t
idVis :: forall a -> a -> a
idVis t x = x :: t Here, idInvis :: forall a. a -> a
idInvis x = x
idVis :: forall a -> a -> a
idVis _ x = x The same stands for the call site. These are explicit type applications: f :: Int -> Int
f = idInvis @Int
g :: Int -> Int
g = idVis Int And these are implicit: f :: Int -> Int
f = idInvis
g :: Int -> Int
g = idVis _ Summary: you may opt in inference for Since we don't call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a much more solid explanation of why With terminology, examples and so on. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I think the terminology can be confusing here. Both of these statements are true: (1)
|
||
As such, the variables at the level of terms exist at the level of types. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, they aren't, however we can't now syntactically distinguish f1 :: Int -> a -> a
f1 t a = a :: t and f2 :: forall a -> a -> a
f2 t a = a :: t Notice that So, we have to accept the presence of term-level variables at the type-level by the renamer, but we reject them later, at type checking (thus, producing the error that we want to describe here). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks. Is there a better way to rephrase this without having to explain internal workings of GHC too much? From an outsider's perspective what does it really look like? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Right, so we definitely shouldn't talk about the GHC renamer, but the user still needs to understand what's going on. It's not just implementation details: it's all observable in the user-facing language. There are several topics that need to be covered: erasure, namespaces, and implicit quantification. I know it's a lot, but we need to sort this out if we want to minimize confusion and misunderstanding. ErasureIn Haskell, every variable is either erased or retained. Let's say I define this type:
I can then call its constructor as We would avoid a great deal of problems if we called those erased arguments and retained arguments. But for historical reasons, we call them type arguments and term arguments (even though The same applies to function declarations. Consider:
The forall-bound variable
We still say that We say "type" when we mean "erased", and we say "term" when we mean "retained". Ideally, when NamespacesWe say that in Haskell we have the "type" namespace and the "term" (or "data") namespace. With If you look at any piece of Haskell code, there are specific rules for coloring each variable red or blue. Example: The rules are roughly as follows:
Historically, it used to be the case that all red-colored variables were retained and all blue-colored variables were erased, so it (kind of) made sense to call them term variables and type variables. But this isn't the case anymore. Consider:
According to our rules, But from the erasure perspective, We are in trouble if we want to stick to our old terminology "type variable" and "term variable", it is inadequate, it no longer describes the reality of Haskell. It's "blue" and "red" from now on, at least within the scope of this GitHub comment :-) Implicit quantificationFinally, to understand why GHC-45510 occurs, we need to remember how implicit quantification works in Haskell. Normally, any variable you use must be explicitly brought into scope. So e.g.
Common sense, right? Well, in type signatures common sense does not apply. Let's say I write this:
What is this
but instead it is assumed that you meant to implicitly quantify over
So the language is designed to guess when you omitted a {-# LANGUAGE ScopedTypeVariables #-}
f :: forall a. Num a => a -> a
f x = g (x+1)
where
g :: a -> a
g = (*2) This is a type-correct definition, where the type signature So the rule is: only out-of-scope variables are implicitly quantified Very important. If the variable is already in scope, just use it! But wait, something doesn't add up... there's one last piece to this puzzle Colorblind quantificationWe have just seen how out-of-scope variables are implicitly quantified, whereas in-scope variables aren't. Now consider this example:
Is the name
(Note the error code: this is the error you're documenting in this PR) The weird thing happens when we don't enable So it turns out that by default, the implicit quantification rule only considers blue-colored variables to be in scope for the purposes of implicit quantification. Based on the declaration With |
||
|
||
If you mistakenly refer to an entity that already exists as a term in your kind signature, you will get | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's cover more contexts in the examples, because the explanation mentions a lot of kind signatures, while the error could be produced by every type construct. That's what internal GHC documentation says about the error:
The (3) can be fixed by |
||
an error about the fact that we cannot do such a thing. | ||
|
||
In the example, the `id` function is already exposed by the Prelude, but it is also a very convenient name to label identifiers. | ||
|
||
You can make sure that GHC understands you're declaring a type-level variable by using the [`forall`] keyword. | ||
|
||
[`RequiredTypeArguments`]: https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/required_type_arguments.html | ||
[`forall`]: https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/explicit_forall.html#extension-ExplicitForAll |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{-# LANGUAGE RequiredTypeArguments #-} | ||
|
||
module ProperUsage where | ||
|
||
import Data.Kind (Type) | ||
|
||
data Set (cxt :: forall id. id -> Type) a = Set [a] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{-# LANGUAGE RequiredTypeArguments #-} | ||
|
||
module WrongUsage where | ||
|
||
import Data.Kind (Type) | ||
|
||
data Set (cxt :: id -> Type) a = Set [a] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
--- | ||
title: Explicit quantification of the type variable | ||
--- |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And nor indirectly! You just have no option to use term-level variable at the type level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, GHC is not yet capable of promoting term variables.