-
Notifications
You must be signed in to change notification settings - Fork 38
Conversation
Good stuff! Looks good at first sight, I'll do a more thorough review during the weekend. |
I like the idea, but feel like the legibility is pretty hard. I'd find it clearer to read something like the following: if optVal match Some(x) {
doSomethingWith(x)
} What do you think? |
@bloodyowl
|
Thanks - I haven't paid too much attention to breadcrumbs/regions, exception cases or doc printing so could probably use some guidance on how to do them best |
@tomgasson Good points yeah. I guess that's where the most sensible tradeoffs are made. |
So to be clear, no I'm still torn between |
Wondering how nested optional record fields look like. Before: // type point = { x:int, y:int, z:option<int> }
// type nested = { origin:option<point>, name:string }
let getZ = nested =>
switch nested.origin {
| None => 0
| Some(point) =>
switch point.z {
| None => 0
| Some(z) => z
}
} after: let getZ = nested =>
if let Some(point) = nested.origin {
if let Some(z) = point.z {
z
} else {
0
}
} else {
0
} @tomgasson notices there's an extra space before |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work. Left a couple of questions w.r.t style/naming, the gist of this PR is actually very good.
I share the same philosophy as Cheng, let's maybe drop the support for when
in V1? We can always add it back later.
Can you maybe add some extra test cases with nested if lets (simple and complex) and ternaries a ? b : c
intermixed?
Cristiano's example is a good start.
src/napkin_core.ml
Outdated
| _ -> | ||
parseIfCore startPos p | ||
|
||
and parseIfExpression p = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we rename this into parseIfOrIfLetExpr
? Makes it clearer what we're possibly parsing here.
src/napkin_core.ml
Outdated
Parser.leaveBreadcrumb p Grammar.ExprIf; | ||
let startPos = p.Parser.startPos in | ||
Parser.expect If p; | ||
let ifBody = parseIfBody startPos p in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would bring parseIfBody
inline here. The general idea is to have something like parseAOrB
indicating that there are two possible things: A and B. parseAOrB
checks the next token and then delegates to the correct function. Example:
and parseIfOrIfLetExpr p =
...
let expr = match p.token with
| Let -> parseIfLetExpr …
| _ -> parseIfExpr …
in
...
src/napkin_core.ml
Outdated
Ast_helper.Exp.case (Ast_helper.Pat.any ()) elseExpr; | ||
] | ||
|
||
and parseIfBody startPos p = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comments, we can bring this inline.
src/napkin_core.ml
Outdated
Ast_helper.Exp.ifthenelse ~loc conditionExpr thenExpr elseExpr | ||
|
||
and parseIfLet startPos p = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency I would rename this to parseIfLetExpr
to indicate that we're going to parse something of the expression flavour.
src/napkin_core.ml
Outdated
Parser.leaveBreadcrumb p Grammar.IfCondition; | ||
(* doesn't make sense to try es6 arrow here? *) | ||
let conditionExpr = parseExpr ~context:WhenExpr p in | ||
Parser.eatBreadcrumb p; | ||
conditionExpr | ||
|
||
and parseIfBranch p = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we rename this to parseThenBranch
for clarity?
src/napkin_core.ml
Outdated
Parser.expect Rbrace p; | ||
blockExpr; | ||
|
||
and parseIfCore startPos p = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Core" is a bit opaque, would it be clearer if we named this just parseIfExpr
?
src/napkin_printer.ml
Outdated
| IfLet (pattern, conditionExpr, guard) -> | ||
let patternDoc = printPattern pattern cmtTbl in | ||
let conditionDoc = printExpressionWithComments conditionExpr cmtTbl in | ||
let guardDocs = match guard with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be removed as we're not supporting guards in v1?
Does this PR suppress the fragile pattern match warning (4)? I guess it wouldn't, correct? |
@yawaramin: If there's an |
@tomgasson thanks. What I'm thinking of is something like: let x = if let Ok(_) = result { 1 } else { 0 } ...desugaring to: let x = switch result {
| Ok(_) => 1
| _ => 0
} ...which indeed seems to trigger the warning. |
@yawaramin: Ah, I see. Yeah I think we should then suppress it then, since the intention here is to allow that but you've raised a few more cases of interest to me let x = if let Ok(_) = result {
1
} else if let Error(_) = result {
2
} else {
0
} ...the let x = if let Ok(_) | Error(_) = result {
1
} else {
0
} ...the else will never be take. The existing warning should let us know |
@tomgasson yeah, in that case we would hit warning 11 (this match case is unused). But just to clarify, you do want to suppress warning 4 for this construct. Another question, will warning 11 wording also be adjusted to reflect the sugar syntax? I.e. it would be confusing to see |
Updated to suppress warning 4. For the wording of 11, I think we need the capability to rewrite warnings as mentioned in #1 |
@tomgasson Merging, thanks for the implementation. Nice work! |
if let
brings pattern matching toif
if let PAT = EXPR { BODY }
The motivation for having any construct at all for this is to simplify the cases that today call for
a switch statement with a single non-trivial case. This is predominately used for unwrapping
option<'t>
values, but can be used elsewhere.Before:
After:
Complex example:
There are >500 occurrences in FB of
| _ => ()
where this form would be more appropriateThis is syntax sugar only, it's implemented similar to
ns.ternary
so that that it should be stable across parsing and printingPrior Art
reasonml/reason#1301
Rust - RFC for
if let
expression rust-lang/rfcs#160. The same justifications apply hereSwift
if case x {}
andif var = foo()
(for reference types)Clojure https://clojuredocs.org/clojure.core/if-let