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 -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/YZMP42SDPIW32GZMVSRJZBEABYYTTRVL/ Code of Conduct: http://python.org/psf/codeofconduct/