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

feat: remove star projection and use-site variance #597

Merged
merged 2 commits into from
Oct 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 2 additions & 42 deletions docs/language/common/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,7 @@ When we use this [class][classes] as a named type, we need to specify the value

In the case of positional type arguments, they are mapped to [type parameters][type-parameters] by position, i.e. the first type argument is assigned to the first [type parameter][type-parameters], the second type argument is assigned to the second [type parameter][type-parameters] and so forth.

If a positional type argument is used, we just write down its value. The value is either

- a [type projection](#type-projection), or
- a [star projection](#star-projection).
If a positional type argument is used, we just write down its value, which is a [type projection](#type-projection).

For example, if we expect a list of integers, we could use the following type:

Expand Down Expand Up @@ -98,7 +95,7 @@ These are the syntactic elements:
- A named type argument (here `T = Int`). This in turn consists of
- The name of the [type parameter][type-parameters] (here `T`)
- An equals sign.
- The value of the type argument, which is still either a [type projection](#type-projection), or a [star projection](#star-projection).
- The value of the type argument, which is still a [type projection](#type-projection).
- A closing angle bracket.

Within a list of type arguments both positional and named type arguments can be used. However, after the first named type arguments all type arguments must be named.
Expand Down Expand Up @@ -136,43 +133,6 @@ SomeSpecialList<Int>

The value of the type argument is just another named type (here `Int`).

###### Use-Site Variance

It is also possible to set the [variance][variance] of a [type parameter][type-parameters] at the use-site, i.e. where we use the containing declaration as a named type. This is only possible, however, if we did not [specify the variance at the declaration-site][declaration-site-variance].

Covariance is denoted by the keyword `out`. If the variance of a [type parameter][type-parameters] is set to `out`, we can only access methods of the class that only use the [type parameter][type-parameters] in the out-position, i.e. as [results][results]. Methods that use the [type parameter][type-parameters] in the in-position, i.e. as [parameters][parameters] are hidden. Here is an example for the syntax:

```txt
SomeSpecialList<out Int>
```

The key element here is the keyword `out` that is added to the type argument. This essentially creates a list of integers that can be read from but that cannot be written to.

Contravariance is denoted by the keyword `in`. If the variance of a [type parameter][type-parameters] is set to `in`, we can only access methods of the class that only use the [type parameter][type-parameters] in the in-position, i.e. as [parameters][parameters]. Methods that use the [type parameter][type-parameters] in the out-position, i.e. as [results][results] are hidden. Here is an example of the syntax:

```txt
SomeSpecialList<in Int>
```

The key element here is the keyword `in` that is added to the type argument. Here we essentially create a list of integers that can be written to but not read from.

##### Star Projection

If we do not want to specify a value for a [type parameter][type-parameters] and just accept everything, we can use a _star projection_. Here is the syntax:

```txt
SomeSpecialList<*>
```

It consists only of the `*`, which we use as the value of the type argument.

The star projection is usually equivalent to the type projections

- `out Any?` (`Any?` is the supertype of everything), or
- `in Nothing` (`Nothing` is the subtype of everything).

If the [type parameter][type-parameters] has [bounds][type-parameter-bounds], however, the star projection denotes that any type within the [bounds][type-parameter-bounds] is acceptable.

### Member Types

A member type is essentially the same as a [named type](#named-types) with the difference that the declaration we refer to is nested inside [classes][classes] or [enums][enums].
Expand Down
17 changes: 7 additions & 10 deletions docs/language/common/variance.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

**Note:** This is an advanced section. Feel free to skip it initially.

Variance deals with the question which generic types are compatible with each other. We explain this concept using the following [class][classes]:
Variance deals with the question, which generic types are compatible with each other. We explain this concept using the following [class][classes]:

```txt
class Stack<T>(vararg initialElements: T) {
Expand Down Expand Up @@ -64,20 +64,17 @@ class Stack<in T> {

## Specifying Variance

The variance of a [type parameter][type-parameters] can either be declared at its [declaration site][declaration-site-variance] or its [use site][use-site-variance]. If it is specified already at the [declaration site][declaration-site-variance], however, [use-site variance][use-site-variance] is no longer available.
The variance of a [type parameter][type-parameters] can only be declared at its [declaration site][declaration-site-variance], using the syntax shown in the following table:

The following table sums up the syntax of [declaration-site variance][declaration-site-variance], where the [class][classes] declaration is changed, and [use-site variance][use-site-variance], where the [type arguments][type-arguments] passed by the [named type][named-types] Refer to the linked documents for more details.

| Desired Variance | Declaration Site | Use Site |
|------------------|----------------------|----------------|
| Invariant | `class Stack<T>` | `Stack<T>` |
| Covariant | `class Stack<out T>` | `Stack<out T>` |
| Contravariant | `class Stack<in T>` | `Stack<in T>` |
| Desired Variance | Declaration Site |
|------------------|----------------------|
| Invariant | `class Stack<T>` |
| Covariant | `class Stack<out T>` |
| Contravariant | `class Stack<in T>` |

[types]: types.md
[named-types]: types.md#named-types
[type-arguments]: types.md#type-arguments
[use-site-variance]: types.md#use-site-variance
[parameters]: parameters.md
[results]: results.md
[classes]: ../stub-language/classes.md
Expand Down
8 changes: 0 additions & 8 deletions src/language/formatting/safe-ds-formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,6 @@ export class SafeDsFormatter extends AbstractFormatter {
this.formatSdsTypeArgumentList(node);
} else if (ast.isSdsTypeArgument(node)) {
this.formatSdsTypeArgument(node);
} else if (ast.isSdsTypeProjection(node)) {
this.formatSdsTypeProjection(node);
}

// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -858,12 +856,6 @@ export class SafeDsFormatter extends AbstractFormatter {
formatter.keyword('=').surround(oneSpace());
}

private formatSdsTypeProjection(node: ast.SdsTypeProjection) {
const formatter = this.getNodeFormatter(node);

formatter.property('variance').append(oneSpace());
}

/**
* Returns whether the type is considered complex and requires special formatting like placing the associated
* parameter on its own line.
Expand Down
6 changes: 1 addition & 5 deletions src/language/grammar/safe-ds.langium
Original file line number Diff line number Diff line change
Expand Up @@ -957,16 +957,12 @@ SdsTypeArgument returns SdsTypeArgument:

interface SdsTypeArgumentValue extends SdsObject {}

interface SdsStarProjection extends SdsTypeArgumentValue {}

interface SdsTypeProjection extends SdsTypeArgumentValue {
variance?: string
^type: SdsType
}

SdsTypeArgumentValue returns SdsTypeArgumentValue:
{SdsStarProjection} '*'
| {SdsTypeProjection} variance=SdsTypeParameterVariance? ^type=SdsType
{SdsTypeProjection} ^type=SdsType
;


Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
segment mySegment(
x: Int < * , in Number , out Number , T = Number > ?
x: Int < Number , T = Number > ?
) {}

// -----------------------------------------------------------------------------

segment mySegment(
x: Int<*, in Number, out Number, T = Number>?
x: Int<Number, T = Number>?
) {}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
segment mySegment(
x: Int < * , in Number , out Number , T = Number>
x: Int < Number , T = Number>
) {}

// -----------------------------------------------------------------------------

segment mySegment(
x: Int<*, in Number, out Number, T = Number>
x: Int<Number, T = Number>
) {}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// $TEST$ no_syntax_error

segment mySegment(
x: Int<*, in Number, out Number, T = Number>?
x: Int<Number, T = Number>?
) {}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// $TEST$ no_syntax_error

segment mySegment(
x: Int<*, in Number, out Number, T = Number>
x: Int<Number, T = Number>
) {}