Closed edemaine closed 1 month ago
I think most of this is pretty good but I'm not sure that &
should lift above statements even when inside the blocks of an if/else
.
When the if/else
block is inside an expression like the example from #1231:
e.children = e.children.map
if & is e.block then block else &
Then it may make sense to me to lift them but still seems to be a bit ambiguous.
One example illustrating some of the strangeness:
if f &
&
The &
in the block would be lifted above the f(&)
The Civet source code already uses this helpful pattern a few times (from #1228):
Do you think that &
should be lifted out of a ternary ?:
, but not out of if/then/else
? This feels asymmetric, which is why I originally added lifting out of if/then/else
. But if/then/else
is way more powerful than ?:
(in particular, multiple indented lines isn't really what we imagined lifting &
out of), so I'm not sure. Some options for restrictions:
then
/else
clause that's one expression (what's currently implemented by this PR)then
/else
clause that's a bare one-liner (if x then y else z
without newlines or indentation)then
/else
clause when the if
is expressionized (I believe it would have a StatementExpression
parent at this stage, so we could detect it). This would include the example with map
, but I believe it would not include the example in my first message (that's an implicit return, not expressionization).then
/else
clause (current behavior). In this case, we should consider not lifting out of ?:
either...We can combine these. E.g., 1+2+3 would be the most restrictive, other than the full restriction of 4. I think ?:
corresponds most closely to either 1+2 or 1+2+3.
I think lifting out of ternary ? :
is fine because it is an expression. Even lifting out of the if &
condition makes sense. But lifting out of a full statement within a containing block seems like too much.
Lifting out of if/else
when it is in a StatementExpression
might be ok but I think for now I'd lean towards Option 4. Keeping ternary lifting seems fine because it is an expression and not a statement.
Also interested in exploring further if we can find a consistent rule but for now "never lifts above a statement" seems relatively clear to understand.
Agreed. However, your point about if &
makes me think of another option:
then
/else
clause only if the if
condition also has a lifted &
.What do you think?
I think this can be implemented because we process &
left to right, so we can check whether this if
has already been "activated" by a lifted condition.
Yet another idea: if we wanted to enable something like & + if cond then &/2 else &/3
, we could generalize to:
then
/else
clause if this would end up lifting to an ancestor to which another &
has already been lifted.The idea for both Options 5 and 6 is, if you have other &
s already lifting to an upper level, then it's less surprising for other &
s to refer to the same &
.
I went ahead and implemented the more conservative Option 5. (Not sure why there's an unnecessary IIFE wrapper there though...) Also happy to revert both if
/else
changes if you'd rather play it safe.
Fixes #1231
&
with binary operators in assignments (e.g.x = &+1
) previously didn't stop at the assignment&
in if/else previously stopped because the then and else clauses are wrapped in blocks; now we continue up if the clause is a one-liner&
in ternary:cond ? & : 0
previously treated: 0
as typing. Now we require no space before: Type
to disambiguate from ternary.: x
was previously treated as[.]: x
. While this is cool, it prevented typing of.
placeholders. I've gone ahead and made.
consistent with&
: it can now be typed via: Type
.I'm a little worried about if/else. I'm sure how to feel about this example:
If you can think of a better rule for if/else, please let me know. (We could require there to be no indentation, i.e.
bare
, but that feels a bit limiting; see https://github.com/DanielXMoore/Civet/issues/1231#issuecomment-2098783437 )