6 ms·
Not the scoping rule. For example, in Haskell: let x = foo in ( case bar of x -> x, x ) This will give `(bar, foo)`: for
by chriswarbo 6y ago
Not the scoping rule. For example, in Haskell:
let x = foo
in (
case bar of
x -> x,
x
)
This will give `(bar, foo)`: for the first element the `x` in the case will match against `bar` and return it, then that `x` will be discarded as we leave its scope; the second element uses the binding of `x` in the `let`.
According to the Python semantics we would get `(bar, bar)`, since we only have one `x` variable. When the case pattern succeeds, the existing `x` is updated to refer to `bar`. Hence we get the `bar` we expect in the first element, but we also get `bar` as the second element, since that `x` variable was updated. (Note that Python guarantees that tuple elements are evaluated from left to right).
- _nosewings_ 6y agoThat's just an inevitable consequence of the fact that, in Python, "scope" is synonymous with "dictionary attached to some object." This is already how for-loops work.
- CogitoCogito 6y agoIt's not "inevitable". They could have required local variables in case statements to be local to that case statement. It would have required changes to the "scope is synonymous with dictionary attached to some object" idea or maybe it would have required a dictionary to be attached to a case statement. I personally think local scope should have been viewed as a hard requirement if they were to introduce this to the language.
- pfalcon 6y agoInterested in block-level scoping in Python? Please post on the python-ideas mailing list. Thanks.
- CogitoCogito 6y agoI'm interested in sane semantics. In this case, that calls for block-level scoping. Those who introduced pattern matching should have understood that the lack of block-level scoping _before_ this PEP does in no way support the continuing of the status quo. The language after this PEP has changed and has turned into one where block-level scoping is appropriate in this case. I'm honestly _not_ interested in block-level scoping in this case because I would _never_ have wanted this PEP to be accepted. This feature was quite controversial on the various python mailing lists, and yet the steering committee accepted it anyway. The steering committee might consider leading with a bit more humility and _not_ accepting such controversial PEPs. This is an example of language devolution and not evolution.
- chriswarbo 6y agoIt occurs to me that there's a nice way to understand this from what's happened in Scala. Scala has always had built-in syntax for pattern-matching, like: foo match { case bar => ... case baz => ... } However, Scala also has a thing called `PartialFunction[InputType, OutputType]`, which is a function defined 'case by case' (it's "partial" because we're allowed to leave out some cases). This is essentially a re-usable set of cases, which we can apply to various values just like calling a function. For example we can write: val f: PartialFunction[A, B] = { case bar => ... case baz => ... } f(foo) Scala also allows us to attach extra methods to certain types of value, via 'implicit classes' (which were added late on in Scala's history, although similar patterns were available before). As of Scala 2.13, the standard library attaches a method called `pipe` to values of every type. The `pipe` method simply takes a function and applies it to this/self. For example: val f: PartialFunction[A, B] = { case bar => ... case baz => ... } foo.pipe(f) However, now that we have these two things (`PartialFunction` and `pipe`), it turns out we don't need explicit syntax for `match` at all! We can always turn: foo match { case bar => ... case baz => ... } Into: foo.pipe({ case bar => ... case baz => ... }) Hence Scala, in a round-about way, has shown us that pattern-matching is essentially a function call. When it comes to Python, it doesn't even need to be a discussion about block scope; it's equally valid to think of this as function scope (like Python already supports), where `case` acts like `lambda`, except we can define a single function as a combination of multiple `case`s (like in the Scala above).
- chriswarbo 6y agoList comprehensions were changed in Python 3 to avoid this, so I don't think it's quite that simple.
- _nosewings_ 6y agoIn Python 3, list comprehensions use generators behind the scenes.
- dragonwriter 6y ago> That's just an inevitable consequence of the fact that, in Python, "scope" is synonymous with "dictionary attached to some object." What object is the scope of a comprehension (in Py 3; in py 2 they don't have their own scope) a dict attached to? And, if you can answer that why could there not be such an object for a pattern match expression?
- _nosewings_ 6y ago> What object is the scope of a comprehension (in Py 3; in py 2 they don't have their own scope) a dict attached to? The generator object that gets created behind the scenes. > And, if you can answer that why could there not be such an object for a pattern match expression? There could be, I suppose, just as there could be for "if" or "for". If Python decided to have lexical scoping everywhere, I would be in favor of that (but then people would complain about breaking changes). In lieu of that, I like the consistency.
- coldtea 6y ago>The generator object that gets created behind the scenes. So like a match object that could get crreated behind the scenes?
- pfalcon 6y agoIf you have automatic block-level scoping, then you have the opposite problem - you need to do extra leg-work to communicate a value to the surrounding scope. Anyway, anyone agrees that block-level scoping is useful. Interested in block-level scoping in Python? Please post on the python-ideas mailing list. Thanks.
- coldtea 6y ago>If you have automatic block-level scoping, then you have the opposite problem - you need to do extra leg-work to communicate a value to the surrounding scope. In the general case, you just declare the variable in the surrounding scope and then affect it in the lower one, no?
- coldtea 6y agoWell, this "inevitable" consequence also infects pattern matching.