On Dec 31, 2019, at 15:52, Greg Ewing <greg.ew...@canterbury.ac.nz> wrote: > > On 1/01/20 11:28 am, Andrew Barnert via Python-ideas wrote: > >> The first is to extend unpacking assignment to target-or-expression lists. >> Like this: >> x, 0, z = vec >> But >> it doesn’t bind anything to the second element; instead, it checks if >> that element is 0, and, if not, raises a ValueError. > > What if you want to use an expression to represent a value to be > matched, rather than a literal? E.g. > > K = 42 > > x, K, z = vec > > The intention here that K is treated as a constant, but with your > semantics K would be bound to element 1 of vec.
Yes. I’m surveying the way other languages deal with this to try to figure out what might fit best for Python. Some languages use special syntax to mark either values or targets: let x, K, let z = vec x, @K, z = vec But the simplest solution is to nothing: you have to stick it in an expression that isn’t a valid target, or It’s a target. And I think that might actually work. If the pattern matching library includes this (or you write it yourself): def val(x): return x … then you just write this: x, val(K), z = vec Which doesn’t seem too bad. And notice that any object with a custom __eq__ will be matched by calling that, so your library can have a decorator that turns an arbitrary function into a matcher, or it can include regex matching like Perl or subclass matching (I’m not sure when you want to mix case class switching and inheritance, but Scala goes out of its way to make that work, so presumably there is a use for it…), and so on. I’m not sure this is the best answer, but it seems at least plausible. >> The second is an “if try” statement, which tries an expression and >> runs the body if that doesn’t raise (instead of if it’s truthy), but >> jumps to the next elif/else/statement (swallowing the exception) if >> it does. > If it truly swallows *any* exception, that's an extremely bad idea, > for all the same reasons that using a bare "except" clause is bad. I haven’t worked through the details on what to swallow yet, because I need to build up a few more detailed examples first. But it’s actually not nearly as bad as a bare except:. First, it’s only guarding a single expression rather than an arbitrary suite. Second, it’s used in a very different context—you’re not catching errors and ignoring them, you’re failing a case and falling over to the next case. (I mean sure, you could misuse it to, say, put a whole try body inside a function and then `if try func(): pass` just to get around a linter warning about bare except, but I’m not too worried about that.) That still may be too bad, and in fact it doesn’t seem likely to be the best option a priori. But I don’t think we can make that call without seeing the worked our examples that I haven’t written yet. > It might be acceptable if it only swallowed a special exception > such as PatternMatchError. > > But then would be fairly specialised towards pattern matching, > so using the general word "try" doesn't seem appropriate. The goal is to use (an extension to) existing unpacking syntax inside if try as the primitive match, and unpacking raises ValueError today, so I think it has to be that or it’s not useful. (Unless we want to change all unpacking errors to a new subclass of ValueError?) And ValueError actually seems right for what “if try” means: we’ve either got an expression with the right value(s). or we’ve got an error from than that tells us there is no such right value(s). Anything else doesn’t seem to make sense in an “if try”. (Although that could easily be a failure of imagination on my part; maybe there are wider uses for the syntax that have nothing to do with pattern matching and don’t even use the walrus operator and I’m just not seeing them because I’m too narrowly focused.) I was worried about TypeError, because that’s what happens when you unpack something that isn’t Iterable. But on further thought, it seems like that’s always going to be a programming error (there’s something wrong with the library, or you just forgot to use the library and tried to match and deconstruct a Notification dataclass instance itself instead of calling matching on it and handling the result), so there’s no problem there. In the other direction, what if the expression you’re trying to match raises a ValueError itself? Or one of the match values raises one from ==? Well, you’ve already got that problem today. Is this code a bug magnet? try: name, domain = email.split('@', 1) except ValueError: raise EmailError(f'invalid address: {email}') We’re not distinguishing between the ValueError from unpacking and any ValueError that may have come from that split method. Plus, in the rare cases where that matters, you should move the split outside the try and stash it in a temporary. The usual way to use if try will already encourage that. Something like: with matching(email.split('@', 1) as m: if try name, val(DOMAIN) :=‘m: local(name) else: remote(domain, email) > At that > point you might be better off with a dedicated "switch" or "case" > construct. Sure, if we’re willing to use up one or two new keywords and design a whole new syntax for them we can obviously make it do whatever we want. But if it’s possible to get what we want more flexibly, with fewer/smaller changes to the language, without being too ugly or hacky, that seems worth pursuing. _______________________________________________ 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/FUH7RT5QTI4FLINCVGUPDLDP3SIWSF4H/ Code of Conduct: http://python.org/psf/codeofconduct/