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
Allow "if let" syntax #6685
Comments
Comment author: @lpw25 For the record "if let" pre-dates Rust, appearing in both Swift and Clojure. It would also be possible to support the related "while let" feature. |
Comment author: @sliquister A readable version of the diff (because whitespace changes are not shown) can be seen at https://github.com/rust-lang/rust/pull/19405/files?w=1 . |
Comment author: @yallop The idea goes back even further than Clojure. For example, Paul Graham's "On Lisp" (1993) defines a macro if-match which is essentially the same as "if let" (http://www.paulgraham.com/onlisptext.html). |
Comment author: @johnwhitington Why not re-use another keyword to make it read better? How about: if e as p then x or if e when p then x A more radical way would be to allow = to be used when exactly one side is a pattern. if e = L (_, Y ) then x But this seems like an abuse of equals! I like 'when' above. |
Comment author: @yallop Overloading = would clash with existing syntax. For example, if Some x = Some y then x is already meaningful. |
Comment author: @johnwhitington Yallop: Quite. Brain not in gear today. I do find the syntax in the original patch hard to read, though, so it would be nice to find something better, if others have the same impression. I think it's seeing 'let' without 'in'. |
Comment author: @damiendoligez "if let" seems really strange to me. What about this: match e with p then x |
Comment author: @whitequark The then-else form is only confusing with normal match, bringing no improvement at all. The else-less form is marginally better. But I dislike both. My original idea was that "if" is essentially a modifier that makes "let" refutable. You would write the code similar to what you would usually write with an if, e.g.: if Some 1 = e then and refactor it to use a pattern match: if let Some x = e then There have been many cases where I wished that let was refutable, but disliked the heaviness of match, which is the motivation for this report. |
Comment author: @lpw25
Whilst it is a little strange, it does have the benefit of existing in a few other languages, so people are more likely to have seen it before. |
Comment author: @yallop It does seem preferable to extend match rather than let here. Currently let is more typically used for destructuring products and match is more typically used for destructuring sums: we say let (x,{l = (y,z)}) = e1 in e2 rather than match e1 with (x,{l = (y,z)}) in e2 but prefer match where there are multiple branches: match e1 with A x -> e2 | B (x, y) -> e3 Using 'let' for branching seems inharmonious with the existing style, and is rather arbitrarily limited to a single branch. I agree with Damien that it seems better to extend match than let, but I'm not sure that it's wise to encourage partial/fragile pattern matching at all. Wouldn't every use of this feature trigger warning 4? Warning 4: this pattern-matching is fragile. On the other hand, if we do end up including it then we should certainly also extend match guards to support 'when let': match e with |
Comment author: mdelahaye I like the "if let" myself. Concerning the warning, I think a different warning should be raise iff the number of constructors is greater than two. It reasonates with the definition of the if-then-else construct in Coq for instance, where the if-then-else is a special case of match where the matched expression's type has two constructors (https://coq.inria.fr/refman/Reference-Manual004.html#sec64) |
Comment author: @yallop Another possibility is to add a form for explicitly-partial matches. For example, we might add a 'match?' form that allows us to write match? x with Some y -> which behaves like the more verbose: match x with We could do the same for let, I suppose, giving something very much like 'if let' in the original proposal: let? Some y = x in There's some precedent for this kind of postfix modifier; for example, we already have 'open!' and 'method!'. |
Comment author: @lpw25
I think that the "?" postfix would more naturally expand to: match x with Which I think this is an improvement on the current practise of writing: match x with because it indicates that the failure is from a match failure, rather than any other kind of assertion. However, this version of the expansion is obviously not a replacement for "if let". |
Comment author: @yallop lpw25: the intention is to ask "does e match p?" without needing to explicitly handle the case where it doesn't. Selectively disabling warnings is useful, but it's not the goal here. We can already achieve the behaviour you propose with match[@ocaml.warning "-8"] x with Some y -> |
Comment author: @lpw25
Sure, but previous postfix things have been about disabling warnings. If the thing you dislike about if match x with Some y then print_string y; |
Comment author: @yallop One drawback of both 'if match' and 'if let' is that the reader has to scan arbitrarily far to the right to disambiguate it from existing valid syntax. The following look rather similar but behave quite differently: if match x with Some y -> y then e |
Comment author: @lpw25 I think I prefer |
Comment author: @yallop I'm also strongly in favour of extending "when" to support pattern matching. However, there are two significant objections to "when let". First, it has the same problem as "if let" and "if match" that the human reader needs to scan ahead arbitrarily to disambiguate two possible parses. There's evidence that this kind of ambiguity causes difficulties in practice from so-called "garden path" sentences in English; when we read things like The old man the boat. then it can take considerable mental effort to find the correct parse. Second, it would be a real shame to limit "pattern-matching when" to a single branch. |
Comment author: @lpw25
I agree that it is not ideal, but I don't think that lets directly within I also don't think your English examples are analogous, because they could not be parsed by an LALR parser. The key difference is that in the case of
I agree, but I can not think of a good syntax for doing so. Also note that the single branch case is expressive enough to handle the multiple branch case: let f = function so it would still be a great improvement over the current situation. |
Comment author: @lpw25 I also don't think that if let Some x ... is almost certainly an if let x ... is almost certainly a In both cases, if the assumption is not true then a warning should be issued (non-exhaustive match in one case and unused case in the other). |
Comment author: @yallop The point of the analogy with English is that syntax that forces the reader to backtrack increases the cognitive load. When you see if let Some x = e ... you need to know whether the condition is that the pattern matches or that the result of the expression is true. LALR doesn't really have anything to do with it: there are all sorts of things that LALR parsers can parse that humans can't and vice versa. Relying on the reader's refutability checking is likely to make things even slower!
I don't think that's true at all. |
Comment author: @lpw25
Yes, and I'm saying that this syntax does not require backtracking from the reader.
Whether this is an
Except that in 99% of cases this check is trivial (is the pattern a variable). I imagine that it is quite difficult (if not impossible) to find a real world piece of code that currently reads
In what way is the example I gave not an illustration that the case of multiple branches can be handled using The need for a local function, and wrapping the result in an option is not ideal, but the point is that you can bind variables in a pattern, match on an expression containing those variables and then if the match succeeds return an expression based on those variables, and if the match fails continue with the original pattern match. This is not currently possible, and would be with the addition of |
Comment author: @yallop
Only if readers can defer determining the scope of bound variables and what happens when matching fails until they're halfway through the code. I think human people trying to understand human intentions like to know these kinds of details as soon as possible.
I think I was mistaken. |
Comment author: @lpw25
Sure. I'm not really disagreeing with you that it is awkward to read, I just think that your English examples require backtracking in a much stronger way than this syntax does, and that the kind of double-take that those examples force you to do does not really apply here. I think the lack of people actually writing |
Comment author: @yallop I think the truth is somewhere in between. Sure, the ambiguity introduced by 'if let' doesn't correspond perfectly in every respect to the difficulties with English garden path sentences. The difficulties are analogous, not identical. The problem is a bit more than the new construct being "awkward to read", though: it's not possible to decide whether you're in an 'if let' or an 'if' until you've read arbitrarily far to the right. You can call this "learning how the code behaves later" rather than "backtracking" if you prefer, but it's still a cognitive burden. As you say, the problem is mitigated by the fact that 'if let' is currently rare, especially with variant types, but it's not eliminated. There's also still the problem that let is a strange choice for a branching construct, since match, not let, is used for matching with branching elsewhere. And there's still the problem that let limits you to a single branch, which seems less than optimal. For what it's worth, I don't really understand what you have against 'match?' and 'let?'. Is it simply the fact that '!' is used to disable warnings? It seems a shame to give up a whole syntactic category for that purpose now that we can accomplish the same thing with attributes . |
Comment author: @lpw25
It's not just that '!' elsewhere is used to remove warnings, it's that there is an obvious remove warnings behaviour for I would also prefer a solution that worked consistently for all of |
This issue has been open one year with no activity. Consequently, it is being marked with the "stale" label. What this means is that the issue will be automatically closed in 30 days unless more comments are added or the "stale" label is removed. Comments that provide new information on the issue are especially welcome: is it still reproducible? did it appear in other contexts? how critical is it? etc. |
Original bug ID: 6685
Reporter: @whitequark
Status: acknowledged (set by @damiendoligez on 2015-01-08T17:26:23Z)
Resolution: open
Priority: normal
Severity: feature
Category: language features
Related to: #6357
Monitored by: mdelahaye @Drup @gasche @ygrek @yallop @hcarty @yakobowski
Bug description
"if let p = e then x" would be equivalent to "match e with p -> x | _ -> ()", and "if let p = e then x else x'" to "match e with p -> x | _ -> x'".
This is inspired by Rust and provided rather significant readability improvement for them: https://github.com/rust-lang/rust/pull/19405/files
I think this feature falls in the same category as "match with exception", syntax-wise. Notably it rearranges the pattern and expression.
I've found that the two match statemenst above is quite common in my code, and "if let" would nicely improve it.
The text was updated successfully, but these errors were encountered: