Skip to content

Add spec section for given patterns reflecting existing semantics #19723

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
63 changes: 60 additions & 3 deletions docs/_spec/08-pattern-matching.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ chapter: 8
| Pattern3
Pattern3 ::= SimplePattern
| SimplePattern {id [nl] SimplePattern}
| GivenPattern
GivenPattern ::= ‘given’ TypePat
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parser considers that this a case of SimplePattern:
https://github.com/lampepfl/dotty/blob/7f410aa1b5cbb38dc4c51da0b5772c7babfaabf0/compiler/src/dotty/tools/dotc/parsing/Parsers.scala#L3026

Also, we should update docs/_docs/internals/syntax.md at the same time, to keep them in sync.

SimplePattern ::= ‘_’
| varid
| Literal
Expand Down Expand Up @@ -274,10 +276,10 @@ object Extractor {
```ebnf
SimplePattern ::= StableId ‘(’ [Patterns ‘,’] [varid ‘@’] ‘_’ ‘*’ ‘)’
```
A _pattern sequence_ ´p_1, ..., p_n´ appears in the following two contexts:

A _pattern sequence_ ´p_1, ..., p_n´ appears in two contexts.
First, in a constructor pattern ´c(q_1, ..., q_m, p_1, ..., p_n)´, where ´c´ is a case class which has ´m+1´ primary constructor parameters, ending in a [repeated parameter](04-basic-definitions.html#repeated-parameters) of type `S*`.
Second, in an extractor pattern ´x(q_1, ..., q_m, p_1, ..., p_n)´ if the extractor object ´x´ does not have an `unapply` method, but it does define an `unapplySeq` method with a result type that is an extractor type for type `(T_1, ... , T_m, Seq[S])` (if `m = 0`, an extractor type for the type `Seq[S]` is also accepted). The expected type for the patterns ´p_i´ is ´S´.
1. In a constructor pattern ´c(q_1, ..., q_m, p_1, ..., p_n)´, where ´c´ is a case class which has ´m+1´ primary constructor parameters, ending in a [repeated parameter](04-basic-definitions.html#repeated-parameters) of type `S*`.
2. In an extractor pattern ´x(q_1, ..., q_m, p_1, ..., p_n)´ _if_ the extractor object ´x´ does _not_ have an `unapply` method, but _defines_ an `unapplySeq` method with a result type that is an extractor type for type `(T_1, ... , T_m, Seq[S])`. If `m = 0`, an extractor type for the type `Seq[S]` is permitted. The expected type for the patterns ´p_i´ is ´S´.

The last pattern in a pattern sequence may be a _sequence wildcard_ `_*`.
Each element pattern ´p_i´ is type-checked with ´S´ as expected type, unless it is a sequence wildcard.
Expand Down Expand Up @@ -307,6 +309,46 @@ All alternative patterns are type checked with the expected type of the pattern.
They may not bind variables other than wildcards.
The alternative pattern matches a value ´v´ if at least one its alternatives matches ´v´.

### Given patterns

```ebnf
GivenPattern ::= TypePat
```

A _given pattern_ introduces an _anonymous given instance_ of the _type_ matched by `TypePat` to the scope of the clause that a given pattern occurs _within_.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what the last part of the sentence means, starting from "to the scope". It does not appear to be grammatically correct, or it's grammatically ambiguous at best. Rephrase?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the corresponding scrutinee is not statically known to have the type TypePat? Is that a compile error or a no-match at run-time?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sjrd I was trying to refer to the scope of the case clause - is the term local scope fine?

I think that in the case that the scrutinee is known statically or not statically should be the same as for a TypePat without a given modifier.

Currently,

val x = 1
val y = x match {
  case y: String => ???
}

gives a compile error and

val x = "asd"

def foo[A](a: A): Unit = {
  a match {
    case s: Int=> 1
  }
}

foo(x)

Is a runtime error.


The rules for the _name_ synthesized for each anonymous instance is defined in the reference.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Link?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, it does not exist in the spec; can I link to the reference in the spec or not?


If multiple names synthesized by the compiler are _not_ unique, the pattern is [illegal](#bound-variables).

###### Example 1

In the following example, the type pattern matches a non-null instance of the `String` class.
The given pattern _introduces_ an _anonymous given instance_ of type `String`, synthesized as `given_String`, to the local scope.

Summoning an implicit of type `String` within the body of the case clause resolves to this new instance; the program prints `"bar"` instead of `"foo"` first.

```scala

given String = "foo"

"bar" match
case given String => println(summon[String]) // "bar" is printed

println(summon[String]) // "foo" is printed
```
###### Example 2

In the following example, a generic type pattern `List[_]` is bound.
This matches a type of `List[t]` where `t` is an anonymous type _within_ the local scope.
`t` is _inferred_ to be a subtype of `Int` within the body of the case clause and can return `Int` without any casts.

```scala
def foo: Int =
List(1) match
case given List[_] => summon[List[?]].head // returns `1`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example does not illustrate that we statically receive an Int. Include a val x: Int = summon[List[Int]].head?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, the return type is Int of the function, but I can introduce a val if it would make it clearer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scala 2

scala> List(42) match { case xs @ (_: List[_]) => xs }
val res1: List[Any] = List(42)

scala 3

scala> List(42) match { case xs @ (_: List[_]) => xs }
val res0: List[Int] = List(42)

I'm not sure if scala 2 is plain wrong here. So any clarification would be helpful.
Oh nm it's REPL output bc:

scala> val ys: List[Int] = List(42) match { case xs @ (_: List[_]) => xs }
val ys: List[Int] = List(42)

but that goes to my point that a) a semi-casual reader might not have a preconceived notion and b) if I try it in the REPL, REPL may let me down, so I rely on your examples before giving up.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, the return type is Int of the function, but I can introduce a val if it would make it clearer.

The reader of the spec does know that, and does not have an IDE or REPL to prove it. So yes, a val with an expected type would make it a lot clearer.

```

### XML Patterns

XML patterns are treated [here](10-xml-expressions-and-patterns.html#xml-patterns).
Expand All @@ -320,6 +362,21 @@ A _sequence pattern_ is a pattern that stands in a position where either (1) a p
A wildcard star pattern `_*` in the rightmost position stands for arbitrary long sequences.
It can be bound to variables using `@`, as usual, in which case the variable will have the type `Seq[A]`.

### Bound variables

A pattern may bind variables. Let `p` be a pattern and `´x_1´, ..., ´x_n´` be the variables with a _name_.

Patterns may:

1. Introduce bound variables directly if _p_ is a:
1. [Variable pattern](#variable-patterns)
1. [Typed pattern](#typed-patterns)
1. [Given pattern](#given-patterns)
1. [Pattern binder](#pattern-binders)
1. Introduce bound variables as a result of a legal _sub-pattern_ in all other cases.

A pattern is illegal if the _name_ of `´x_i´` is not unique.

### Irrefutable Patterns

A pattern ´p´ is _irrefutable_ for a type ´T´, if one of the following applies:
Expand Down