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

Restrictions on disjunctive patterns #44

Open
JohnReppy opened this issue Sep 7, 2020 · 16 comments
Open

Restrictions on disjunctive patterns #44

JohnReppy opened this issue Sep 7, 2020 · 16 comments

Comments

@JohnReppy
Copy link
Contributor

The Definition of Successor ML specifies that the pattern pat1 | pat2 must be irredundant, which is defined that there must exist a value that is matched by pat2 but not pat1. After discussions with Dave MacQueen, I wonder if the restriction should be that pat1 and pat2 are required to be disjoint; i.e., that there is no value that is matched by both patterns.

This restriction has the property that it makes the | pattern constructor commutative, which I think is intuitive.

@RobertHarper
Copy link

RobertHarper commented Sep 7, 2020 via email

@ratmice
Copy link
Contributor

ratmice commented Sep 7, 2020

@RobertHarper I believe that this issue is about matches like (Foo(x), y) | (x, Baz(y)) => x,
not affecting the normal match ::= pat => exp ⟨| match⟩. I also noted a paper referring to some issues with these disjunctive patterns in #42.

Maybe I'm misinterpreting, but I don't think an expression like x | _ => () makes sense since x isn't bound in all the patterns.

Edit: Updated my first case to actually be sensical.

@RobertHarper
Copy link

RobertHarper commented Sep 7, 2020 via email

@MatthewFluet
Copy link
Contributor

I'm not sure. The advantage of specifying it asymmetrically is that it naturally corresponds to the naive implementation of just expanding all the or-patterns into sequential matches, where one would only have a warning if pat2 was irredundant with pat1.

@ratmice
Copy link
Contributor

ratmice commented Sep 8, 2020

A good argument in favor is that the restriction proposed could be relaxed,
However if we take the relaxed behavior as standard we cannot restrict it further.

Given that this would fix that other issue without the addition of a new warning, which it seems only occurs where overlapping disjunctive patterns meet pattern guards, I would be inclined to argue in favor of it.

@dmacqueen
Copy link

dmacqueen commented Sep 8, 2020 via email

@RobertHarper
Copy link

RobertHarper commented Sep 8, 2020 via email

@rossberg
Copy link
Member

rossberg commented Sep 9, 2020

Wouldn't such a restriction harm usability? You want overlapping or patterns for similar reasons that you want overlapping cases. Imagine a large datatype like

datatype t = C1 of int | C2 of t2 | ... | CN of tN

and a pattern like the following:

SOME ((C1 n, _) | (_, n)) => e(n)

With the proposed restriction, I'd have to write this as

SOME ((C1 n, _) | ((C2 _ | C3 _ | ... | CN _), n)) => e(n)

which seems rather silly and tedious, especially for larger N.

@RobertHarper
Copy link

RobertHarper commented Sep 9, 2020 via email

@dmacqueen
Copy link

dmacqueen commented Sep 9, 2020 via email

@MatthewFluet
Copy link
Contributor

Well, the dynamic semantics of OR-patterns at https://github.com/SMLFamily/Successor-ML/blob/master/definition/dyncor.tex#L1016 pretty clearly specifies that the matches are tried in left-to-right order. (Of course, a static semantics that rejected non-commutative OR-patterns would make the specifics of the dynamic semantics less relevant.)

And, there is clearly an ordering imposed on the match compiler based on the order of the matches.

@rossberg
Copy link
Member

Even if the static semantics rejected overlapping or-patterns, their dynamic semantics would still need to be ordered, due to the presence of nested matches and guards, which (unfortunately) can have observable side effects. So they still wouldn't be commutative.

@rossberg
Copy link
Member

rossberg commented Sep 10, 2020

Another consideration is that such a restriction would complicate the static semantics considerably. So far, the Definition never needs to define coverage for patterns, since it's only needed to implement an informal warning. If we were to elevate that to an error in some cases, we'd have to go through the trouble of formalising coverage.

Moreover, we'd run into the known issue with exception constructors, where overlap cannot generally be decided during type checking of a pattern itself:

functor F (exception E1; exception E2) =
  struct
    fun f x = case x of (E1 | E2) => true | _ => false
  end

structure A1 = F (exception E1; exception E2)       (*) okay
structure A2 = F (exception E1; exception E2 = E1)  (*) not okay?

In other words, such a requirement isn't compositional.

@ratmice
Copy link
Contributor

ratmice commented Sep 10, 2020

I would at least like prefer if come up with a better name than disjunctive patterns at least, since disjunction is commutative and this is not, something like ordered intersection patterns (but preferably less terrible 🤷)

@MatthewFluet
Copy link
Contributor

"left-biased OR-patterns"?

@rossberg
Copy link
Member

FWIW, SML's regular disjunction operator (orelse) is not commutative either, with its non-strict left-to-right evaluation.

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

No branches or pull requests

6 participants