Jordan still kinda wants the class-matcher to be on Function.prototype, but seems okay with the current proposal (make Function.prototype[Symbol.customMatcher] just invoke the function itself).
If clauses/patterns
Assuming binding semantics are well-established (looks like we're in agreement, see below), everyone's fine with using if() patterns and not doing separate if clauses in match().
Binding Visibility outside of patterns
Successful match exposes bindings to if/for/while, uncontentious
Do failed matches expose bindings?
In an if, does a failed match still expose the bindings to the else?
This is necessary to allow for negative patterns to be used; whether you write if(x is let y) or if(x is not let y) is a code-organization choice
Jordan thinks all possibilities are reasonable to explain/define.
With bare is, Jordan is opposed to wide exposure of bindings
In if(x is let y):
Possible scope exposure, in order, is "just pattern", if block, if/else block, or full containing scope
Ron agrees with description of scenarios
Negative tests become useless if you don't expose to else.
Ron prefers surrounding block context scope; every other let decl today is block-scoped
Except old-style for() itself which is scoped to the head (+loop?)
Consistency argument - if an if can use an expr and expose it to the else, why not extract it out to the larger block?
Generally, if you early-exit in the if(){...}, there's no difference between using an else or not. Shouldn't create a difference.
Refactoring hazard if they're not exposed to the outer scope.
Also generally thinks it's somewhat useful. Trivial case x is let y is silly, but complex pattern match can be useful.
Tab is cool with if exposing to block context, but confused about loops
Ron says it's consistent to have loops retain tight scope
Ron might be okay with a bare if leaking to block scope, while if+else is scoped to those blocks
Tab thinks that's weird.
Jordan too.
Jordan points out we could ban binding patterns in patterns used in plain expression statements.
Jordan somewhat concerned that a top-level x is let y; leaking bindings would be confusing.
Some back and forth about whether it's confusing or dangerous for top-level is to create bindings.
If they leak to the block context, there's no shadowing potential (it'd be a redeclaration error).
Conclusion is that Ron and Tab feel that "patterns establish bindings in the surrounding block scope" is the necessary logical outcome of several other properties we want. Jordan follows the logic but is still somewhat uncomfortable, will think more on it.
is in an if() head should expose its bindings to the if's block. (if(x is let y) { log(y); })
Whether your if() test is expressed positively or negatively is a code organization choice, not a semantic one; you should always be able to swap your if and else blocks just by negating your if() condition. This implies that bindings in the if() head need to be visible to the else block as well. If also implies that failed patterns still expose their bindings.
When you early-exit an if() block, whether you use an else or not doesn't make a difference. This implies that that bindings in the if() head should be visible to the nearest block scope.
Being able to pull an expression out of a test and set it to a temp var ahead of time is useful/necessary code organization. If you can't do that with patterns, kinda sucks. This implies that a bare x is let y; expression statement should also expose bindings to the nearest block scope.
Short circuiting
Short-circuiting is still important to maintain, to let you hide expensive tests or ones that could error.
Bindings should be able to occur multiple times in a pattern, so long as only one results from a successful pattern
(x or let y) and (z or let y)
if both x and z fail, y is TDZ-poisoned
if both x and z succeed, y establishes a binding from its first pattern, then runtime errors on the second
if only one succeeds, y gets the binding from that one
(x or let y or z) and if(y)
if x fails, y is TDZ-poisoned (but the if() won't run anyway due to short-circuit)
if z fails, y is bound and fine to use (if() still won't run)
if x and z both succeed, everything's great
Precedent for this: named match groups in regexp alternatives. Previously illegal, now (proposed?) to be acceptable.
Can talk about this top-down or bottom-up:
Collect all bindings in the pattern, then set each as they're encountered. If you try to set something that's already set, runtime error. If nothing ever sets it, it's TDZ-poisoned.
Define for each combinator how it combines the bindings from its sub-patterns. Skipped bindings can be overridden by other branches.
Meeting 2023-09-18
Attendees: Tab, Jordan, Ron
Function matchers
class
-created matcher.Function.prototype[Symbol.customMatcher]
just invoke the function itself).If clauses/patterns
if()
patterns and not doing separate if clauses inmatch()
.Binding Visibility outside of patterns
if
, does a failed match still expose the bindings to theelse
?if(x is let y)
orif(x is not let y)
is a code-organization choiceis
, Jordan is opposed to wide exposure of bindingsif(x is let y)
:if
block,if/else
block, or full containing scopeelse
.for()
itself which is scoped to the head (+loop?)if
can use an expr and expose it to theelse
, why not extract it out to the larger block?if(){...}
, there's no difference between using anelse
or not. Shouldn't create a difference.x is let y
is silly, but complex pattern match can be useful.if
exposing to block context, but confused about loopsif
leaking to block scope, whileif
+else
is scoped to those blocksx is let y;
leaking bindings would be confusing.is
to create bindings.is
in anif()
head should expose its bindings to theif
's block. (if(x is let y) { log(y); }
)if()
test is expressed positively or negatively is a code organization choice, not a semantic one; you should always be able to swap yourif
andelse
blocks just by negating yourif()
condition. This implies that bindings in theif()
head need to be visible to theelse
block as well. If also implies that failed patterns still expose their bindings.if()
block, whether you use anelse
or not doesn't make a difference. This implies that that bindings in theif()
head should be visible to the nearest block scope.x is let y;
expression statement should also expose bindings to the nearest block scope.Short circuiting
(x or let y) and (z or let y)
y
is TDZ-poisonedy
establishes a binding from its first pattern, then runtime errors on the secondy
gets the binding from that one(x or let y or z) and if(y)
y
is TDZ-poisoned (but theif()
won't run anyway due to short-circuit)y
is bound and fine to use (if()
still won't run)