<p>In some languages that support pattern matching, there is a feature that allows you to match against multiple patterns at once. This is called <a href="https://peps.python.org/pep-0636/#or-patterns" rel="noreferrer">&quot;or patterns&quot;</a> in Python 3.10. For example, you can write:</p>
<pre class="lang-python prettyprint-override"><code>match x:
    case 0 | 1:
        print(&quot;x is 0 or 1&quot;)
    case 2:
        print(&quot;x is 2&quot;)
    case _:
        print(&quot;x is something else&quot;)
</code></pre>
<p>Other languages that support this feature include <a href="https://reference.wolfram.com/language/ref/Alternatives.html" rel="noreferrer">Mathematica</a>, <a href="https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html#multiple-patterns" rel="noreferrer">Rust</a>, and <a href="https://v2.ocaml.org/manual/patterns.html#sss:pat-or" rel="noreferrer">Ocaml</a>.</p>
<p>However, this feature is missing in Haskell. There isn't even a GHC extension for it. I'm wondering why this is the case.</p>
<p>What are the pros and cons of supporting &quot;or patterns&quot; in pattern matching?</p>
<hr />
<p>Update: RubenVerg pointed out in the comments that there is a <a href="https://github.com/knothed/ghc-proposals/blob/master/proposals/0522-or-patterns.rst" rel="noreferrer">GHC proposal</a> for this feature. But it doesn't allow you to bind variables in or patterns.</p>

<haskell><pattern-matching>
Pros and cons of supporting "or patterns" in pattern matching
CC BY-SA 4.0
2023-05-28T12:10:01.690Z
Note that [or patterns are coming to GHC](https://github.com/ghc-proposals/ghc-proposals/pull/522)
RubenVerg
<h2>Variable binding</h2>
<p>In languages that let you bind variables in patterns, it might complicate type checking a little, but you could probably also treat it as syntactic sugar. In Haskell, for example, suppose you had the hypothetical syntax:</p>
<pre class="lang-haskell prettyprint-override"><code>data Expr = EAdd Expr Expr | EMul Expr Expr | EInt Integer

f :: Expr -&gt; Something
f e = case e of
        EAdd e1 e2 | EMul e1 e2 -&gt; do_something_with e1 e2
        EInt i -&gt; do_something_else_with i
</code></pre>
<p>You would need to ensure that <code>e1</code> and <code>e2</code> were bound in both patterns and had the same type. There is some design space to explore here; for example, is it okay to bind a variable in one case but not the other if that variable is unused?</p>
<p>One possible way to desugar this:</p>
<pre class="lang-haskell prettyprint-override"><code>f e = let g e1 e2 = do_something_with e1 e2
      case e of
        EAdd e1 e2 -&gt; g e1 e2
        EMul e1 e2 -&gt; g e1 e2
        EInt i -&gt; do_something_else_with i
</code></pre>
<p><a href="https://mercurylang.org/" rel="noreferrer">Mercury</a> fully supports this style of pattern matching. Here is the equivalent of the above Haskell code; for those who aren't familiar with Prolog-like syntax, in a goal, <code>,</code> means &quot;logical and&quot; and <code>;</code> means &quot;logical or&quot;.</p>
<pre><code>:- type expr
    ---&gt; eadd(expr,expr)
    ;    emul(expr,expr)
    ;    eint(int).

:- pred p(expr,something).
:- mode p(in,out) is det.

p(E, Output) :-
    (
        ( E = eadd(E1,E2)
        ; E = emul(E1,E2)
        ),
        do_something(E1, E2, Output)
    ;
        E = eint(I),
        do_something_else(I, Output)
    ).
</code></pre>
<p>The Mercury compiler identifies this as a mode-correct and deterministic switch.</p>

CC BY-SA 4.0
2023-05-29T00:58:51.773Z
2023-05-29T00:58:51.773Z
Pseudonym
CC BY-SA 4.0
2023-05-29T10:32:23.200Z
Swift does this as well -- see [this example](https://swiftfiddle.com/aruykt3qsjdyziohd75r42gfnm). If you change the first `_` to `let y`, or try to add `.baz(let x)` to the first case, it'll complain that the variables aren't identical between patterns in the same case.
Bbrk24
<h3>Advantages</h3>
<ul>
<li><p><strong>Concise and readable code:</strong> With &quot;or patterns&quot;, you can express multiple alternative patterns in a single line, making the code more compact and easier to read. It can help reduce verbosity and improve code maintainability.</p>
</li>
<li><p><strong>Reduced code duplication:</strong> When you have several patterns that require the same behaviour, combining them with &quot;or patterns&quot; eliminates the need to duplicate the associated code.</p>
</li>
</ul>
<h3>Disadvantages</h3>
<ul>
<li><p><strong>Complexity and potential confusion:</strong> Introducing &quot;or patterns&quot; adds complexity to the pattern matching system. It can make the language more difficult to understand for newcomers and increase the potential for errors or misunderstandings.</p>
</li>
<li><p><strong>Ambiguity and overlapping patterns:</strong> With &quot;or patterns&quot;, there is a possibility of creating ambiguous or overlapping patterns that can lead to unexpected behaviour or conflicts. As mentioned in <a href="/a/1161">the other answer</a>, there is ambiguity with the bitwise OR operator in Python.</p>
</li>
<li><p><strong>Compiler optimisations:</strong> The absence of &quot;or patterns&quot; allows the compiler to make certain optimisations, such as exhaustiveness checks, which can help catch incomplete pattern matches at compile-time. Supporting &quot;or patterns&quot; would require more sophisticated analysis and potentially impact the efficiency of these optimisations.</p>
</li>
</ul>

CC BY-SA 4.0
2023-05-28T19:17:32.787Z
2023-05-28T21:03:26.813Z
2023-05-28T21:03:26.813Z
The Thonnu
CC BY-SA 4.0
2023-05-28T23:26:31.493Z
`The absence of "or patterns" allows the compiler to make certain optimisations, such as exhaustiveness checks.`

How so? One "or" pattern is the same as two "non-or" patterns.
Longinus
CC BY-SA 4.0
2023-05-28T23:49:38.523Z
`Ambiguity and overlapping patterns` this can happen even in the absence of or-patterns.
Longinus
<h1>Ambiguity</h1>
<p>At least in Python's selected syntax for this, there's semantic overload on whether you're allowing both 0 and 1 to enter the case, or if you're trying to match on the result of <code>(0 | 1)</code> (bitwise OR).</p>

CC BY-SA 4.0
2023-05-28T18:57:19.253Z
2023-05-28T18:57:19.253Z
kouta-kun
CC BY-SA 4.0
2023-05-28T23:48:44.713Z
If the language allows `(0 | 1)` to be a pattern which invokes an infix operator, I'd argue it's already failed. Expressions shouldn't be in pattern position, only constants and bound variables should. Trying to overlap expressions and patterns is just a recipe for disaster if you ask me.
Silvio Mayolo
CC BY-SA 4.0
2023-05-28T23:54:29.083Z
@SilvioMayolo How would you match over dependent sequences like `$x :: #(x × 2)` without expressions?
Longinus
CC BY-SA 4.0
2023-05-28T23:59:55.123Z
@Longinus Honestly, I've never even thought about doing something like that. Is there a language that allows such a dependent sequence? Because I'd love to look into it if so.
Silvio Mayolo
CC BY-SA 4.0
2023-05-29T00:06:31.787Z
@SilvioMayolo In Mathematica, everything is an expression, including patterns.
alephalpha
CC BY-SA 4.0
2023-05-29T00:22:35.437Z
@SilvioMayolo I disagree in part. You could match on 0x0001, 0x0002, 0x0004, 0x0008 etc, or you could match on (1 << 0), (1 << 1), (1 << 2), etc. There are good arguments for allowing matching on constant *expressions*, but perhaps not in a dynamic language like Python.
Pseudonym
CC BY-SA 4.0
2023-05-29T00:54:27.927Z
@SilvioMayolo [Egison](https://www.egison.org/) is the first that comes to mind.
Longinus
CC BY-SA 4.0
2023-05-29T01:13:14.507Z
Swift avoids this by making its "or" patterns use commas instead of pipes -- so you'd have `case 0, 1:` in this case.
Bbrk24
# Pros and cons of supporting "or patterns" in pattern matching
Posted by: username_0 on 2023-05-28
In some languages that support pattern matching, there is a feature that allows you to match against multiple patterns at once. This is called "or patterns" in Python 3.10. For example, you can write:
match x:
    case 0 | 1:
        print("x is 0 or 1")
    case 2:
        print("x is 2")
    case _:
        print("x is something else")

Other languages that support this feature include Mathematica, Rust, and Ocaml.
However, this feature is missing in Haskell. There isn't even a GHC extension for it. I'm wondering why this is the case.
What are the pros and cons of supporting "or patterns" in pattern matching?

Update: RubenVerg pointed out in the comments that there is a GHC proposal for this feature. But it doesn't allow you to bind variables in or patterns.
**Tags:** haskell pattern-matching
> **Comments:**
> **username_1**: Note that [or patterns are coming to GHC](https://github.com/ghc-proposals/ghc-proposals/pull/522)  
---
## 3 answers
### Answer by username_2 on 2023-05-29. Score: 7
Variable binding
In languages that let you bind variables in patterns, it might complicate type checking a little, but you could probably also treat it as syntactic sugar. In Haskell, for example, suppose you had the hypothetical syntax:
data Expr = EAdd Expr Expr | EMul Expr Expr | EInt Integer

f :: Expr -> Something
f e = case e of
        EAdd e1 e2 | EMul e1 e2 -> do_something_with e1 e2
        EInt i -> do_something_else_with i

You would need to ensure that e1 and e2 were bound in both patterns and had the same type. There is some design space to explore here; for example, is it okay to bind a variable in one case but not the other if that variable is unused?
One possible way to desugar this:
f e = let g e1 e2 = do_something_with e1 e2
      case e of
        EAdd e1 e2 -> g e1 e2
        EMul e1 e2 -> g e1 e2
        EInt i -> do_something_else_with i

Mercury fully supports this style of pattern matching. Here is the equivalent of the above Haskell code; for those who aren't familiar with Prolog-like syntax, in a goal, , means "logical and" and ; means "logical or".
:- type expr
    ---> eadd(expr,expr)
    ;    emul(expr,expr)
    ;    eint(int).

:- pred p(expr,something).
:- mode p(in,out) is det.

p(E, Output) :-
    (
        ( E = eadd(E1,E2)
        ; E = emul(E1,E2)
        ),
        do_something(E1, E2, Output)
    ;
        E = eint(I),
        do_something_else(I, Output)
    ).

The Mercury compiler identifies this as a mode-correct and deterministic switch.
> **Comments:**
> **username_3** (2023-05-29): Swift does this as well -- see [this example](https://swiftfiddle.com/aruykt3qsjdyziohd75r42gfnm). If you change the first `_` to `let y`, or try to add `.baz(let x)` to the first case, it'll complain that the variables aren't identical between patterns in the same case.  
---
### Answer by username_4 on 2023-05-28. Score: 4
### Advantages

* **Concise and readable code:** With "or patterns", you can express multiple alternative patterns in a single line, making the code more compact and easier to read. It can help reduce verbosity and improve code maintainability.
* **Reduced code duplication:** When you have several patterns that require the same behaviour, combining them with "or patterns" eliminates the need to duplicate the associated code.

### Disadvantages

* **Complexity and potential confusion:** Introducing "or patterns" adds complexity to the pattern matching system. It can make the language more difficult to understand for newcomers and increase the potential for errors or misunderstandings.
* **Ambiguity and overlapping patterns:** With "or patterns", there is a possibility of creating ambiguous or overlapping patterns that can lead to unexpected behaviour or conflicts. As mentioned in [the other answer](/a/1161), there is ambiguity with the bitwise OR operator in Python.
* **Compiler optimisations:** The absence of "or patterns" allows the compiler to make certain optimisations, such as exhaustiveness checks, which can help catch incomplete pattern matches at compile-time. Supporting "or patterns" would require more sophisticated analysis and potentially impact the efficiency of these optimisations.
> **Comments:**
> **username_5** (2023-05-28): `The absence of "or patterns" allows the compiler to make certain optimisations, such as exhaustiveness checks.`

How so? One "or" pattern is the same as two "non-or" patterns.  
> **username_5** (2023-05-28): `Ambiguity and overlapping patterns` this can happen even in the absence of or-patterns.  
---
### Answer by username_6 on 2023-05-28. Score: 3
# Ambiguity

At least in Python's selected syntax for this, there's semantic overload on whether you're allowing both 0 and 1 to enter the case, or if you're trying to match on the result of `(0 | 1)` (bitwise OR).
> **Comments:**
> **username_7** (2023-05-28): If the language allows `(0 | 1)` to be a pattern which invokes an infix operator, I'd argue it's already failed. Expressions shouldn't be in pattern position, only constants and bound variables should. Trying to overlap expressions and patterns is just a recipe for disaster if you ask me.  
> **username_5** (2023-05-28): @SilvioMayolo How would you match over dependent sequences like `$x :: #(x × 2)` without expressions?  
> **username_7** (2023-05-28): @Longinus Honestly, I've never even thought about doing something like that. Is there a language that allows such a dependent sequence? Because I'd love to look into it if so.  
> **username_0** (2023-05-29): @SilvioMayolo In Mathematica, everything is an expression, including patterns.  
> **username_2** (2023-05-29): @SilvioMayolo I disagree in part. You could match on 0x0001, 0x0002, 0x0004, 0x0008 etc, or you could match on (1 << 0), (1 << 1), (1 << 2), etc. There are good arguments for allowing matching on constant *expressions*, but perhaps not in a dynamic language like Python.  
> **username_5** (2023-05-29): @SilvioMayolo [Egison](https://www.egison.org/) is the first that comes to mind.  
> **username_3** (2023-05-29): Swift avoids this by making its "or" patterns use commas instead of pipes -- so you'd have `case 0, 1:` in this case.  
---
