Feedback: Guards and static/instance pattern declarations

Stephen Colebourne scolebourne at joda.org
Sun Jan 24 14:18:30 UTC 2021


The semantic approach of turning boolean expressions into guard
patterns seems reasonable. But it seems to me that the goal should be
to integrate boolean expressions and patterns as completely as
possible, rather than trying to make them disjoint. Currently:

* the use of & is nasty given its meaning of evaluating both sides
*  true(<booleanExpr>) and false(<booleanExpr>) seem like overkill
("new stuff wants to look new")

// this is the current proposal
 case NumBox(int i) & true(i == 0):
 case NumBox(int i) & true(i > 6) & true(i < 10):

// this seems much more desirable
 case NumBox(0):
 case NumBox(int i) && i > 6 && i < 10:
 case NumBox(int i) && (i < 6 || i > 10):

ie. a guard pattern should just look like a boolean expression. I
think this will be less confusing in the long run - IDEs can highlight
a pattern context in italic/bold/colour if desired.

By introducing a pattern context with a leading keyword, the rule
would be that everything to the right of the keyword is interpreted
using pattern context rules. Params would be used to control the
boundaries of the pattern context if needed.

Rather than letting instanceof operator precedence drive the syntax,
it would be better to add case expressions (everything to the right of
`case` is in the pattern context):

  if (x case NumBox(int i) && i > 6 && i < 10) {...}

(Ideally you'd drop support for type patterns after instanceof in Java
17 - I'm sure people would cope since it has only been there a few
months)

More complex cases would use params:

  if ((x case NumBox(int i) && i > 6) ||
      (x case NumBox(double d) && d > 6) ) {...}

----
A separate part of the discussion is around how to use (and define)
pattern methods that take an input argument. I can see why you'd like
to add them to the language, but most of the examples provided so far
tend to make code less readable, not more. I'm not yet convinced that
user-defined patterns will be net positive to the daily use of the
language.

As the language gets more complex I fear that the developer's brain is
being asked to infer too much from ASCII punctuation. Here, the double
args list in patternMethodCall()() is difficult to follow.

 case regex(REGEX_PATTERN)(var regexMatch1, var regexMatch2):

I'd like to suggest an additional keyword between the two args-lists
makes it much more palatable to read. Here is one possible way:

 if (x case regex(REGEX_PATTERN) match (var regexGroup1, var
regexMatch2)) { ... }

 switch (m) {
  case regex(REGEX_PATTERN_A) match (var regexMatch1) -> ...
  case regex(REGEX_PATTERN_B) {
     match (var regexMatch1) -> ...
     match (var regexMatch1, var regexMatch2) -> ...
   }
  }

In the above, `match` would always be allowed to precede the result
part of the pattern match, but `match` would be omitted with built in
pattern. ie. the first is the verbose form and the second is the
sugar:

  case match (StringBuffer b) -> ...
  case StringBuffer b -> ...

(and no, I don't think this is just because user-defined patterns are
new syntax - I suggest this because I genuinely think the double
arg-list in patternMethodCall()() is hard to read).


----
In summary, I think pattern contexts are a good direction to explore,
but I think doubling down on them and unifying with normal boolean
expressions ought to be possible and readable to avoid creating two
disjoint syntax universes.

By contrast, the user-defined pattern method examples so far look
mostly like a case of "look what we could do" rather than "is this
really beneficial" If user-defined pattern methods are added, I think
an extra keyword to separate obtaining the pattern from matching the
pattern makes it hugely more readable.

Stephen


More information about the amber-dev mailing list