<p>In a language with standard let and if expressions, what should be the their precedence such that they can be nested without parentheses?</p>
<p>For example, I want to be able to parse both of these expressions:</p>
<pre><code>if let a = b in c then d else e
</code></pre>
<pre><code>let a = if b then c else d in e
</code></pre>
<hr />
<p>I am using <a href="https://github.com/lalrpop/lalrpop" rel="noreferrer">lalrpop</a> to generate a parser. My attempt looks like this:</p>
<pre><code>grammar;

pub Expr: () = {
    #[precedence(level=&quot;0&quot;)]
    Name =&gt; { println!(&quot;Name: {}&quot;, &lt;&gt;); },

    #[precedence(level=&quot;1&quot;)]
    #[assoc(side=&quot;right&quot;)]
    &quot;let&quot; &lt;name:Name&gt; &quot;=&quot; &lt;value:Expr&gt; &quot;in&quot; &lt;body:Expr&gt; =&gt; { println!(&quot;Let: {}&quot;, name); },

    #[precedence(level=&quot;2&quot;)]
    &quot;if&quot; &lt;check:Expr&gt; &quot;then&quot; &lt;then:Expr&gt; &quot;else&quot; &lt;els:Expr&gt; =&gt; { println!(&quot;If&quot;); },
};

Name: &amp;'input str = r&quot;[a-zA-Z_]+'?&quot;;
</code></pre>
<p>However, this can only parse the first of the above expressions. If I swap the precedence of the last two rules, then it can only parse the second!</p>
<p>What am I missing?</p>

<parsing><expressions><precedence>
Relative precedence of let and if expressions
CC BY-SA 4.0
2023-06-09T11:28:08.560Z
I don't believe this is a case of precedence conflict. The first keyword (`if` or `let`) allows to unambiguosly identify the applicable rule. The same goes for the nested expressions, so having a `let` in the `if`'s condition is perfectly fine
abel1502
<h1>Make <code>if let</code> a single construct</h1>
<p>This is what Swift does (though it uses <code>,</code> rather than <code>in</code>):</p>
<pre class="lang-swift prettyprint-override"><code>if condition { /* okay */ }
if (condition) { /* okay */ }
if let a = b { /* okay */ }
if let a = b, condition { /* okay */ }
if (let a = b) { /* error */ }
</code></pre>

CC BY-SA 4.0
2023-06-09T13:31:05.437Z
2023-06-09T13:31:05.437Z
Bbrk24
<p>Adding to <a href="https://languagedesign.stackexchange.com/a/1454/89">@Bbrk24's answer</a>, Rust has <a href="https://doc.rust-lang.org/book/ch06-03-if-let.html" rel="nofollow noreferrer"><code>if let</code></a> as a separate language construct with its own semantics, as opposed to just combining the semantics of an <code>if</code> expression with a <code>let</code> expression. Particularly, it allows the <code>let</code> binding to be a <a href="https://doc.rust-lang.org/book/ch18-02-refutability.html" rel="nofollow noreferrer">refutable pattern</a>, unlike <code>let</code> in other contexts. (The same applies for <code>let</code> bindings in other conditional contexts, e.g. <code>while let</code>.)</p>
<p>For example, if <code>some_opt</code> is an <a href="https://en.wikipedia.org/wiki/Option_type" rel="nofollow noreferrer">option</a>:</p>
<pre class="lang-rust prettyprint-override"><code>if let Some(x) = some_opt {
    // ...
}
</code></pre>
<p>The statement <code>let Some(x) = some_opt;</code> by itself would be invalid since the pattern <code>Some(x)</code> is refutable; it doesn't match the value <code>None</code>. But this is allowed in an <code>if let</code> statement, where the variable <code>x</code> is bound only inside the conditional branch, and the conditional branch is executed only when the pattern does match (so that <code>x</code> does have a value).</p>
<p>If your language has <a href="https://en.wikipedia.org/wiki/Pattern_matching" rel="nofollow noreferrer">pattern matching</a> and/or destructuring assignments already, then it may be worth including this construct in your language. In that case, there is no need to resolve any precedence issue between <code>if</code> and <code>let</code> when parsing <code>if let ...</code>, since this becomes just a single expression.</p>

CC BY-SA 4.0
2023-06-09T16:53:04.957Z
2023-06-09T16:53:04.957Z
kaya3
CC BY-SA 4.0
2023-06-09T17:08:10.813Z
Swift `if let x = y` is specifically shorthand for `if case Optional.some(let x) = y`, so it is more semantically similar to Rust than to the let-in statement the OP has. However, Swift's shorthand is more _syntactically_ similar to the OP, and the question is about parsing, not semantics.
Bbrk24
<p>As pointed out in the comments, this actually doesn't seem to be a precedence issue. I can get the desired behavior by removing the precedence annotations altogether:</p>
<pre><code>grammar;

pub Expr: () = {
    Name =&gt; { println!(&quot;Name: {}&quot;, &lt;&gt;); },
    &quot;let&quot; &lt;name:Name&gt; &quot;=&quot; &lt;value:Expr&gt; &quot;in&quot; &lt;body:Expr&gt; =&gt; { println!(&quot;Let: {}&quot;, name); },
    &quot;if&quot; &lt;check:Expr&gt; &quot;then&quot; &lt;then:Expr&gt; &quot;else&quot; &lt;els:Expr&gt; =&gt; { println!(&quot;If&quot;); },
};

Name: &amp;'input str = r&quot;[a-zA-Z_]+'?&quot;;
</code></pre>
<p>So the answer to the question is they should be at the <em>same</em> level.</p>

CC BY-SA 4.0
2023-06-09T16:03:56.333Z
2023-06-09T16:03:56.333Z
Panda
# Relative precedence of let and if expressions
Posted by: username_0 on 2023-06-09
In a language with standard let and if expressions, what should be the their precedence such that they can be nested without parentheses?
For example, I want to be able to parse both of these expressions:
if let a = b in c then d else e

let a = if b then c else d in e


I am using lalrpop to generate a parser. My attempt looks like this:
grammar;

pub Expr: () = {
    #[precedence(level="0")]
    Name => { println!("Name: {}", <>); },

    #[precedence(level="1")]
    #[assoc(side="right")]
    "let"  "="  "in"  => { println!("Let: {}", name); },

    #[precedence(level="2")]
    "if"  "then"  "else"  => { println!("If"); },
};

Name: &'input str = r"[a-zA-Z_]+'?";

However, this can only parse the first of the above expressions. If I swap the precedence of the last two rules, then it can only parse the second!
What am I missing?
**Tags:** parsing expressions precedence
> **Comments:**
> **username_1**: I don't believe this is a case of precedence conflict. The first keyword (`if` or `let`) allows to unambiguosly identify the applicable rule. The same goes for the nested expressions, so having a `let` in the `if`'s condition is perfectly fine  
---
## 3 answers
### Answer by username_0 on 2023-06-09. Score: 6 | Accepted answer
As pointed out in the comments, this actually doesn't seem to be a precedence issue. I can get the desired behavior by removing the precedence annotations altogether:
grammar;

pub Expr: () = {
    Name => { println!("Name: {}", <>); },
    "let"  "="  "in"  => { println!("Let: {}", name); },
    "if"  "then"  "else"  => { println!("If"); },
};

Name: &'input str = r"[a-zA-Z_]+'?";

So the answer to the question is they should be at the same level.
---
### Answer by username_2 on 2023-06-09. Score: 3
Make if let a single construct
This is what Swift does (though it uses , rather than in):
if condition { /* okay */ }
if (condition) { /* okay */ }
if let a = b { /* okay */ }
if let a = b, condition { /* okay */ }
if (let a = b) { /* error */ }
---
### Answer by username_3 on 2023-06-09. Score: 3
Adding to @Bbrk24's answer, Rust has if let as a separate language construct with its own semantics, as opposed to just combining the semantics of an if expression with a let expression. Particularly, it allows the let binding to be a refutable pattern, unlike let in other contexts. (The same applies for let bindings in other conditional contexts, e.g. while let.)
For example, if some_opt is an option:
if let Some(x) = some_opt {
    // ...
}

The statement let Some(x) = some_opt; by itself would be invalid since the pattern Some(x) is refutable; it doesn't match the value None. But this is allowed in an if let statement, where the variable x is bound only inside the conditional branch, and the conditional branch is executed only when the pattern does match (so that x does have a value).
If your language has pattern matching and/or destructuring assignments already, then it may be worth including this construct in your language. In that case, there is no need to resolve any precedence issue between if and let when parsing if let ..., since this becomes just a single expression.
> **Comments:**
> **username_2** (2023-06-09): Swift `if let x = y` is specifically shorthand for `if case Optional.some(let x) = y`, so it is more semantically similar to Rust than to the let-in statement the OP has. However, Swift's shorthand is more _syntactically_ similar to the OP, and the question is about parsing, not semantics.  
---
