[e-lang] MatchBindExpr wierdness
Mark S. Miller
markm at cs.jhu.edu
Mon May 1 01:30:40 EDT 2006
The MatchBindExpr is an odd duck. In all other circumstances *except* the
MatchBindExpr, match-failure causes a non-local exit, preventing control flow
from continuing into the scope where variable defined by the pattern would be
visible. This is even true for typical uses of MatchBindExpr as well, such as:
if ([i, j] =~ [x, y :char]) {
... x ... y ...
} else {
# x and y are not in scope
}
However, because the MatchBindExpr evaluates to a boolean, if this boolean
isn't immediately used by an 'if' as above, then the question arises of what
values should 'x' and 'y' have when the pattern match fails. Dean has
identified two stances to take: "incremental" binding vs. "atomic" binding.
Let's say 'i' and 'j' both currently hold integers. Integers do not
auto-coerce to characters, causing the above match to fail when attempting to
match the 'y :char' sub-pattern. The issue is, this failure occurs after the
'x' pattern match has succeeded, binding 'x' to the value of 'i'. In the scope
following the MatchBindExpr as a whole, 'y' is bound to a broken reference.
But what is 'x' bound to? Incremental binding says that all variables already
bound stay bound. Atomic binding says that all variable bound by a failed
pattern match are broken in the scope following the MatchBindExpr.
E-on-Java currently seems to provide atomic binding:
? [1,2] =~ [x, y :char]
# value: false
? y
# value: <ref broken by problem: <ClassCastException: \
# Integer doesn't coerce to a Character>>
? x
# value: <ref broken by problem: <ClassCastException: \
# Integer doesn't coerce to a Character>>
If MatchBindExpr provided incremental binding, the value of 'x' would have
been 1. Although the above session is consistent with atomic binding, further
examination shows that E-on-Java's current behavior is just buggy:
? var q := 0
# value: 0
? [1,2] =~ [x ? (q := fn{x}; true), y :char]
# value: false
This pattern match succeeds or fails exactly when the previous one would, and
results in the same bindings for 'x' and 'y'. However, within the context
where 'x' is bound to 1, it creates and stores a closure which captures the
'x' variable. Since the 'x' variable that was captured was bound to 1 and is
final, it should still be bound to 1 when we call 'q'.
? q()
# value: <ref broken by problem: <ClassCastException: \
# Integer doesn't coerce to a Character>>
This reveals that the E-on-Java's current MatchBindExpr implementation, on the
failure case, breaks all variables defined by the pattern, regardless of
whether they've already been bound.
To implement correct incremental binding, we'd need to somehow skip breaking
those variable already successfully bound. To implement correct atomic
binding, we'd have to consider the 'x' within the pattern and the 'x' visible
after the MatchBindExpr to be different 'x' variables. The first 'x' is
successfully bound to 1, whereas the second 'x' becomes bound to a broken
reference.
My next email will show Kevin's proposed expansion of MatchBindExpr to Dean's
proposed trinary-define expression. By taking MatchBindExpr out of Kernel-E,
Kernel-E itself no longer needs to break variables on match failure, and so
avoids the whole incremental vs. atomic weirdness. Kevin's proposed expansion
of MatchBindExpr provides atomic binding. We should also examine expansions
that provide incremental binding.
--
Text by me above is hereby placed in the public domain
Cheers,
--MarkM
More information about the e-lang
mailing list