Skip to content
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

Add documentation on kind signatures, which ones are displayed, and why #437

Merged
merged 12 commits into from
Jul 12, 2021
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ the most up-to-date version of this file.
* Build against v0.14.3 of the PureScript compiler
* Migrate to GitHub Actions for CI (#435 by @thomashoneyman)
* Fix an internal typo (#432 by @i-am-the-slime)
* Added a section on kind signatures (#437 by @JordanMartinez)

## v0.8.2

Expand Down
97 changes: 97 additions & 0 deletions static/help-docs/users.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,100 @@ called "Kleisli" in some contexts. This may also change in the future; see
[`sequence`]: https://pursuit.purescript.org/packages/purescript-foldable-traversable/docs/Data.Traversable#t:Traversable
[Modules page in the documentation repository]: https://github.com/purescript/documentation/blob/master/language/Modules.md
[`Star`]: https://pursuit.purescript.org/packages/purescript-profunctor/docs/Data.Profunctor.Star#t:Star

## Kind Signatures

### Explicit and Inferred

Data, newtype, type synonym, and type class declarations all have kind
signatures. These signatures are either explicit (i.e. developer-defined)
or implicit (i.e. compiler-inferred). For example

```purescript
-- Explicit kind signature

thomashoneyman marked this conversation as resolved.
Show resolved Hide resolved
data ExplicitFoo :: forall k. k -> Type
data ExplicitFoo a = ExplicitFoo

{- Kind signature inferred by the compiler for the below type:
data ImplicitFoo :: forall k. k -> (Type -> Type) -> Type -}

thomashoneyman marked this conversation as resolved.
Show resolved Hide resolved
data ImplicitFoo a f = ImplicitFoo (f Int)
```

### Merging Documentation Comments

Since both the kind signature declaration and the data/newtype/type/class
declaration can have documentation comments, both will be merged together
with a newline separating them. For example, the below code's doc comments...
```purescript
-- | Kind signature declaration documentation comment
data ExplicitFoo :: forall k. k -> Type
-- | Data declaration documentation comment
data ExplicitFoo a = ExplicitFoo
```
... will be rendered as
```
Kind signature declaration documentation comment
Data declaration documentation comment
```

### Interesting kinds are displayed; Uninteresting kinds are not

The following design choice should make it easier for new learners
to learn the language by limiting their exposure to these more
advanced language features.

By default, all kind signatures, whether explicitly-declared by the developer
or inferred by the compiler, will only be displayed if the kind signatures
are considered "interesting." Put differently, "uninteresting" kind signatures will not be displayed.

An "uninteresting" kind signature is one that follows this form:
- `Type`
- `Constraint`
- `Type -> K` where `K` refers to an "uninteresting" kind signature

Here's another way to think about it: kind signatures are considered
"uninteresting" if all of their type parameters' kinds have kind `Type`.

#### Examples of "uninteresting" kind signatures

Consider the following examples of "uninteresting" kind signatures. Each
kind signature is considered "uninteresting" because
1. it does not have type parameters
2. if it has type parameters, each type parameters' kind is kind `Type`

```purescript
data TypeOnly :: Type
data TypeOnly

class IntentionallyEmpty :: Constraint
class IntentionallyEmpty

class Bar :: Type -> Type -> Constraint
class Bar a b where
convert :: a -> b
```

In short, if you see a data, newtype, type synonym, or type class declaration
that has type parameters and it does not have a kind signature, then you
know by default that the kind of each type parameter is kind `Type`.

#### Examples of "interesting" kind signatures

Consider the following examples of "interesting" kind signatures.
Each kind signature is "interesting" because it has at least one
type parameter whose kind is something other than kind `Type`:
```purescript
-- the "k" part makes this kind signature "interesting"
data PolyProxy :: forall k. k -> Type
data PolyProxy a = PolyProxy

-- the `(Type -> Type)` part makes this kind signature "interesting"
data FunctorLike :: (Type -> Type) -> Type -> Type
data FunctorLike f a = FunctorLike (f Int) a

-- every type parameter makes this kind signature "interesting"
class TypeLevelProgrammingFunction :: Symbol -> Row Type -> Row Type -> Constraint
class TypeLevelProgrammingFunction sym row1 row2 | sym row1 -> row2
```