<div dir="auto">My opinion, for several years, has been that the Haskell designers erred in making outermost patterns lazy by default in let and where, and at the top level. I believe they should have gone with something much simpler:<div dir="auto"><br></div><div dir="auto">1. Patterns are strict by default.</div><div dir="auto">2. Variables are lazy by default.</div><div dir="auto">3. Patterns at the top level must be marked lazy.</div><div dir="auto">4. (With bang patterns) Variables at the top level may not be marked strict.</div><div dir="auto"><br></div><div dir="auto">That would harmonize the way patterns are handled in let and where with the way they're handled in function arguments and case expressions, as well as removing the outermost-pattern special case which makes the *strictness* of inner patterns surprising. Unfortunately, I don't think there's much chance of any of these changing in Haskell.</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, Jan 2, 2023, 6:00 PM Tom Ellis <<a href="mailto:tom-lists-haskell-cafe-2017@jaguarpaw.co.uk">tom-lists-haskell-cafe-2017@jaguarpaw.co.uk</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Consider the following:<br>
<br>
    let x0 = (undefined, ()) ; ((_, _), _) = x0 in x0 `seq` ()<br>
    ()<br>
<br>
but then combining the patterns into an @-pattern, which really feels<br>
like it shouldn't change semantics:<br>
<br>
    let x1@((_, _), _) = (undefined, ()) in x1 `seq` ()<br>
    -> undefined<br>
<br>
Does this strike anyone else as odd?  The reason for this behaviour is<br>
that<br>
<br>
    let x@p = rhs in body<br>
<br>
is translated to<br>
<br>
    case rhs of ~(x@p) -> body<br>
<br>
according to section 3.12 of The Report[1] which is then translated to<br>
<br>
    (\x -> body) (case rhs of x@p -> x)<br>
<br>
if p binds no variables, according to section 3.17.3 of The Report[2],<br>
and then to<br>
<br>
    (\x -> body) (case rhs of p -> (\x -> x) rhs)<br>
<br>
which is equivalent to<br>
<br>
    (\x -> body) (case rhs of p -> rhs)<br>
<br>
Putting it all together<br>
<br>
    let x0 = (undefined, ()) ; ((_, _), _) = x0 in x0 `seq` ()<br>
<br>
desugars as<br>
<br>
    (\x0 -> x0 `seq` ())<br>
    (let v = (undefined, ()) in case v of ((_, _), _) -> v)<br>
<br>
which evaluates as<br>
<br>
    let v = (undefined, ())<br>
    in case v of ((_, _), _) -> ()<br>
<br>
<br>
This seems very odd to me.  Why should combining two non-diverging<br>
patterns into an @-pattern cause divergence?  Specifically, the<br>
translation from<br>
<br>
    let x@p = rhs in body<br>
<br>
to<br>
<br>
    case rhs of ~(x@p) -> body<br>
<br>
seems highly dubious.  It comes from the more general rule translating<br>
<br>
    let p = rhs in body<br>
<br>
to<br>
<br>
    case rhs of ~p in body<br>
<br>
but it seems to interact badly with @-patterns.  It seems like the<br>
rule for @-patterns should be something like<br>
<br>
    let x@p = rhs in body<br>
<br>
translates to<br>
<br>
    case rhs of ~(x@(~p)) in body<br>
<br>
Then the original expression containing x1 would not diverge.<br>
<br>
Does anyone else have a view on this matter?<br>
<br>
Tom<br>
<br>
<br>
[1] <a href="https://www.haskell.org/onlinereport/exps.html#sect3.12" rel="noreferrer noreferrer" target="_blank">https://www.haskell.org/onlinereport/exps.html#sect3.12</a><br>
<br>
[2] <a href="https://www.haskell.org/onlinereport/exps.html#sect3.17.3" rel="noreferrer noreferrer" target="_blank">https://www.haskell.org/onlinereport/exps.html#sect3.17.3</a><br>
_______________________________________________<br>
Haskell-Cafe mailing list<br>
To (un)subscribe, modify options or view archives go to:<br>
<a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe" rel="noreferrer noreferrer" target="_blank">http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe</a><br>
Only members subscribed via the mailman list are allowed to post.</blockquote></div>