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

Matching pattern languages #528

Closed
josh11b opened this issue May 11, 2021 · 9 comments
Closed

Matching pattern languages #528

josh11b opened this issue May 11, 2021 · 9 comments
Labels
leads question A question for the leads team

Comments

@josh11b
Copy link
Contributor

josh11b commented May 11, 2021

We have so far been trying to design a single pattern syntax to be used across variable declarations, function declarations, and match statements. This is turning out to be a big constraint, and we have been bumping into the problems with this approach recently. This approach is only common in functional languages with very different needs. I think it is time to ask the question of whether we should treat these as separate problems in order to better address their individual needs.

Lets look at what each context needs:

  • fn function declaration
    • Most important: specifying the types of parameters
    • And then: name binding, defaults/optional, labels
    • Concern with "consistent pattern grammar" approach: fn F(Int) doesn't match user's expectations
    • Exotic: Matching or destructuring values. Rare for users to want this (not available in C/C++) and interfere with solving the fn F(Int) problem.
  • var variable declaration
    • Most important: name binding
    • And then: types, destructuring tuples/structs optionally with renaming
  • match statements
    • Most important: dispatch based on runtime values
    • Most common: equal to value, in range, in set
      • These cases correspond to C/C++ switch and don't need name bindings or types
    • Sum type dispatch would use name binding
    • Dynamic type tests would use both types and name binding
    • Cases are refutable and ordered
  • guard statements
    • Most important: unwrapping optional and/or result types
    • Refutable patterns like match statements, but focused on the sum type and dynamic type cases that need name bindings

My contention is that these are pretty different use cases, each with unique concerns to accommodate.

@dabrahams
Copy link

A concern with fn and var that's not mentioned here is that the current semantics imply the creation of innocuous-looking scenarios that have no choice but to trap at runtime, e.g.:

    fn f(Type t) {
      var fnty(Type x)->Type = fnty(Int)->t;
   }

and this one, which (until you apply constant folding), needs to behave the same way for the same reasons, which is far from obvious to the casual reader (or even someone like me with his fingers deep in the implementation):

var fnty(Type x)->Type = fnty(Int)->Int;

@zygoloid
Copy link
Contributor

I think we don't want "normal" (unguarded) var declarations to contain refutable patterns, so I think we should reject those fnty examples. (I think we probably also don't want fn declarations to contain refutable patterns, but clearly match statements need to support such patterns and guard declarations would presumably require them.) How much impact does that have on the question of whether we use the same syntax for those different contexts? I'd expect we can at least use a smaller set of grammar rules for irrefutable patterns (in particular, we presumably don't need constants).

@dabrahams
Copy link

I think we probably also don't want fn declarations to contain refutable patterns

Depends if you want overloading == pattern matching or not. If that's what you want, there's nothing wrong in principle with having a refutable overload as long as the whole overload set is irrefutable. I personally wouldn't design pattern matching into overloading in this way—a match statement inside a function body is a much more straightforward way to get the same effect—but this is what I (and others) have understood the design intent to be.

How much impact does that have on the question of whether we use the same syntax for those different contexts?

🤷 if you don't care that the same syntax might imply different semantics, none. I don't think the real question is what the syntax should be, but whether we're really trying to uphold the principle that "it's all just pattern matching." If the answer is yes, keeping the syntax the same makes sense. If not, maybe there's more flexibility.

@geoffromer
Copy link
Contributor

I think it is time to ask the question of whether we should treat these as separate problems in order to better address their individual needs.

I don't think we should treat this as a yes-or-no question. It's probably not realistic to insist that any code that's valid in a match pattern is also valid in a function signature, a var declaration, a guard statement, etc., but I don't think that means we should treat those constructs as being all completely unrelated to each other. So I think the question is something more like "what do we want to be the syntactic and semantic connections between them?", but I'm not sure we can usefully answer such an open-ended question at this stage, other than perhaps with general principles.

The main principle that I would like us to try to stick to is that function parameter lists, match cases, and var/let declarations should all be subsets of a single coherent pattern language. In particular, we shouldn't have a syntax that is valid in more than one of those contexts, but has a different meaning depending on which context it appears in. Conversely, we shouldn't have a meaning that is expressible in more than one of those contexts, but has to be expressed using different syntax depending on the context.

Most if not all of the problems discussed so far seem like they can be resolved consistently with that principle, e.g. by disallowing refutable patterns in variable declarations and function signatures. Do we have any examples of problems that don't seem to be solvable under that principle?

@github-actions

This comment was marked as outdated.

@github-actions github-actions bot added the inactive Issues and PRs which have been inactive for at least 90 days. label Aug 18, 2021
@jonmeow jonmeow added the leads question A question for the leads team label Aug 10, 2022
@github-actions github-actions bot removed the inactive Issues and PRs which have been inactive for at least 90 days. label Aug 11, 2022
@github-actions

This comment was marked as outdated.

@github-actions github-actions bot added the inactive Issues and PRs which have been inactive for at least 90 days. label Nov 9, 2022
@josh11b josh11b removed the inactive Issues and PRs which have been inactive for at least 90 days. label Nov 9, 2022
@josh11b
Copy link
Contributor Author

josh11b commented Nov 9, 2022

What I've heard so far:

  • We'd like the syntax to be consistent, but different subsets are allowed in different contexts.
  • We aren't going to allow refutable patterns in fn/var/let, and that is going to eliminate a class of bugs / surprises.
  • A thread on #syntax starting 2022-09-16 considered separating the expression and pattern grammars. In particular, the only unintroduced pattern is a bound name identifier : expression.

@zygoloid
Copy link
Contributor

Some of these questions have been answered by approved proposal #2188. In particular:

I think the remaining question here is, do we have syntactic restrictions on some of these pattern contexts? That is, do we allow only a subset of the pattern grammar, depending on which kind of pattern we're parsing? And if so, what are the restrictions that we impose? We've spun some of that out into separate questions:

I think given the discussion on those issues, we can answer the remaining question as: yes, we may have additional restrictions for some or all pattern contexts, depending on the needs of that context. We'll decide what restrictions make sense for different contexts separately.

@chandlerc
Copy link
Contributor

Agreed and calling this decided.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
leads question A question for the leads team
Projects
None yet
Development

No branches or pull requests

6 participants