Skip to content

Name-based pattern matching rules #1805

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

Closed
odersky opened this issue Dec 15, 2016 · 3 comments
Closed

Name-based pattern matching rules #1805

odersky opened this issue Dec 15, 2016 · 3 comments

Comments

@odersky
Copy link
Contributor

odersky commented Dec 15, 2016

This is an attempt to come up with a sane set of rules for name-based pattern matching. To recall: In dotty, name-based is all we have, there is no special treatment of type classes anymore.

The purpose of the rules is, given a pattern P(P1, .., Pn), to produce the expected types PT1,..., PTn against which the argument patterns P1, .., Pn will be matched. Let X be the expected type of the whole pattern match.

  1. If the companion object P contains a member def unapply(x: T): U such that X conforms to T then

    1. If U is a subtype of a product type Product{n}[T1, ... Tn] with arity n then PTi = Ti, for i = 1,..,n.

    2. If U has parameterless (def or val) members isEmpty: Boolean and get: V,
      then:

      1. if there is exactly one argument (P1) then PT1 = V.
      2. if there are zero or more than one arguments, then V must derive from a product type
        Product{n}[V1, ..., Vn] and PTi = Vi, for i = 1,..,n.
    3. If U is of type Boolean and n = 0, OK

    4. Otherwise error.

  2. If the precondition (1) does not hold (i.e. no unapply method with the right argument type), but the companion object P contains a member def unapplySeq(x: T): U where X conforms to T then

    1. If U derives from Seq[V] for some type V, then PTi = V for i = 1,..,n, or PTn = V* if Pn is a repeated parameter.
    2. If U has parameterless (def or val) members isEmpty: Boolean and get: S,
      where S <: Seq[V] for some type V, then PTi = V for i = 1,..,n, or PTn = V* if Pn is a repeated parameter.
    3. Otherwise error.

Note 1: There are some subtleties with overloading which are skimmed over here. See the comment of typer.Applications#trySelectUnapply for a more detailed explanation.

Note 2: A consequence of the rules is that a selector type may match patterns of different arities using the same unapply. One arity could trigger a product based selection (using rule 1.i), the other a get/isEmpty selection (using rule 1.ii). Scala parser combinators actually exercise that functionality. A test case is in pos/Patterns.scala.

odersky added a commit to dotty-staging/dotty that referenced this issue Dec 15, 2016
This implements the rules laid down in scala#1805.
@Blaisorblade
Copy link
Contributor

These rules don't make Option special, but still make Seq special. Could that change in the same spirit?
Instead of checking if some type derives from Seq[V], could one check if it has a concrete type member Elem and take that as V?

@smarter
Copy link
Member

smarter commented Dec 15, 2016

To recall: In dotty, name-based is all we have, there is no special treatment of type classes anymore.

I think you mean case classes

@odersky
Copy link
Contributor Author

odersky commented Dec 21, 2016

@Blaisorblade Seq is the prescribed internal type for repeated parameters, which is what unapplySeq supports. So I don't think we need to generalize it further.

@odersky odersky closed this as completed Dec 21, 2016
OlivierBlanvillain added a commit to dotty-staging/dotty that referenced this issue Apr 6, 2017
Product pattern use to:
- have a `<: Product` requirement
- compute the arity of a pattern by looking at `N` in a `ProductN` superclass.

This commit changes `<: Product`, instead we look for a `_1` member. The arity is determined by inspecting `_1` to `_N` members instead.

---

Here another attempt to formalize Dotty's pattern-matching (base on scala#1805). This covers the *Extractor Patterns* [section of the spec](https://www.scala-lang.org/files/archive/spec/2.12/08-pattern-matching.html#extractor-patterns). Dotty support 4 different extractor patterns: Boolean Pattern, Product Pattern, Seq Pattern and Name Based Pattern.

Boolean Pattern

- Extractor defines `def unapply(x: T): Boolean`
- Pattern-matching on exactly `0` patterns

Product Pattern

- Extractor defines `def unapply(x: T): U`
- `N > 0` is the maximum number of consecutive (parameterless `def` or `val`) `_1: P1` ... `_N: PN` members in `U`
- Pattern-matching on exactly `N` patterns with types `P1, P2, ..., PN`

Seq Pattern

- Extractor defines `def unapplySeq(x: T): U`
- `U` has (parameterless `def` or `val`) members `isEmpty: Boolean` and `get: S`
- `S <: Seq[V]`
- Pattern-matching on any number of pattern with types `V, V, ..., V`

Name Based Pattern

- Extractor defines `def unapply(x: T): U`
- `U` has (parameterless `def` or `val`) members `isEmpty: Boolean` and `get: S`
- If there is exactly `1` pattern, pattern-matching on `1` pattern with type `S`
- Otherwise fallback to Product Pattern on type `U`

In case of ambiguities, *Product Pattern* is preferred over *Name Based Pattern*.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants