On Tue, Dec 31, 2019 at 02:28:26PM -0800, Andrew Barnert via Python-ideas wrote:
> if try 0, y, z := vec:
> # do yz plane stuff
> elif try x, 0, z := vec:
> # do xz plane stuff
> elif try x, y, 0 := vec:
> # do xy plane stuff
> elif x, y, z := vec:
> # do slow/imprecise/whatever 3D stuff
> else:
> raise TypeError(f'{vec} is not a 3-vector!')
Comments below.
> Alternatively, this could just be a try expression that can be used
> anywhere: it’s truthy if evaluating doesn’t raise, falsey if it does.
> But I don’t think it’s needed anywhere but if/elif.
Have you read the exception catching PEP?
https://www.python.org/dev/peps/pep-0463/
> Anyway, neither of these features seems very useful on its own. (It
> should be obvious how to rewrite that example as something just as
> readable that just unpacks and then checks the values with normal if.)
Indeed. The ordinary "if" version is two lines shorter, although you
lose the chance to provide a custom exception message.
> But I think the two of them together will allow a pure-library
> implementation of pattern matching syntax that reads nicely and can do
> everything most other languages do—in particular, concisely and
> readably pattern match and deconstruct dataclasses (and other types
> with an opt-in protocol or registry, but dataclasses would work out of
> the box the way Scala case classes, Swift structs, etc. do).
I don't know about "reads nicely".
I really like the F# pattern matching syntax, even if it would require
two new keywords (match, when) and an arrow symbol (F# uses -> but
Python could use a colon). That reads nicely to me (and also supports
guard clauses, although that's not needed in your example):
# F# syntax
match vec with
| 0, y, z -> yz_expression
| x, 0, z -> xz_expression
| x, y, 0 -> xy_expression
| x, y, z -> xyz_expression
| _ -> fail
In F# matching is an expression. I don't know if that's an advantage or
disadvantage.
I find the F# explanation for what pattern matching is all about *much*
more understandable than the Haskell version -- even the "Gentle
Introduction".
https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/pattern-matching
https://www.haskell.org/tutorial/patterns.html
Also relevant: Coconut has pattern matching:
https://marnee.silvrback.com/a-bit-of-railway-oriented-programming-with-coconut
https://coconut.readthedocs.io/en/master/DOCS.html#match
We don't need anything new to get prototype pattern matching.
Patterns are first class values in SNOBOL (and maybe in Icon?). Using
that as inspiration, we could create Pattern objects to do the work, and
a match function that we call like this:
# Fail() is a function that raises an exception;
# __ is a special predefined Pattern that always matches.
result = match(vec, # object to match
Pattern(0, 'y', 'z'), handle_yz,
Pattern('x', 0, 'z'), handle_xz,
Pattern('x', 'y', 0), handle_xy,
Pattern('x', 'y', 'z'), handle_xyz,
__, Fail('not a vector')
)
The match() function would simply pass `vec` to each Pattern object in
turn, until one matches, then calls the handle_* callback function with
the variables stored in the pattern object after a successful match.
def match(value, *args):
if len(args)%2 != 0: raise TypeError
for pattern, callback in pairs(args):
if pattern.match(value):
callback(**pattern.vars())
The implementation of match() is trivial: all the pattern matching
goodies are in the Pattern object:
- if the match succeeds, the Pattern object stores the matched variables
('x', 'y', 'z' in this case) and returns itself;
- if the match fails, it returns None.
- pattern.vars() returns a dict with the matched variables.
The pros: no new syntax required! Not even the walrus operator. This
could, in principle, work all the way back to Python 1.5 if you were so
inclined.
But the cons...
* it's tediously verbose
* it's a nuisance having to pre-define the functions handle_* ahead of
time (in Ruby, you would just pass a block)
* but then the same applies for the old "use a dict dispatch table in
as a switch/case" idiom
* having to quote the names in the Pattern constructor is a pain.
But as a prototype, I think I've seen worse ideas.
--
Steven
_______________________________________________
Python-ideas mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/[email protected]/message/YZMP42SDPIW32GZMVSRJZBEABYYTTRVL/
Code of Conduct: http://python.org/psf/codeofconduct/