That's not too bad if it would be a syntax error to either set or shadow an existing variable with the match statement. Apparently it isn't, which is concerning. Personally I think I may have preferred something like:
match status:
case == not_found: # Check for equality
...
match status:
case as not_found: # bind status to variable not_found
...
At least the former should be an option instead of using a dotted name IMO.
You know, a lot of potentially confusing behavior would be avoided if programming languages had the sense to make variables read-only by default and disallow variable shadowing altogether.
I really like shadowing, since it prevents me making mistakes all over the place by referring to the wrong thing. If I introduce a new name, I have two names cluttering up my namespace, and might pick the wrong one by mistake; for example if I validate 'myInput' to get 'myValidatedInput', later on I can still refer to 'myInput', which would be a mistake, and may end up bypassing the validation. On the other hand, I can shadow 'myInput' with the validated result, meaning that (a) I can no longer refer to the value I no longer want, (b) there's only one suitable "input" in scope, so it's easier to do things correctly, (c) I don't have to juggle multiple names and (d) it's pure and immutable, and hence easier to reason about than statements (like 'del(myInput)' or 'myInput = validate(myInput)'.
>I really like shadowing, since it prevents me making mistakes all over the place by referring to the wrong thing. If I introduce a new name, I have two names cluttering up my namespace, and might pick the wrong one by mistake;
Compared to having two versions of the same name, one shadowing another?
def neighbourhood(position):
return map(
lambda position: EDGE if position is None else position.absolute,
position.neighbours
)
The inner lambda is shadowing the name 'position'. This does two things:
1) It declares that the lambda doesn't depend on the argument of 'neighbourhood'
2) It prevents us referring to that argument by mistake
Compare it to a non-shadowing version:
def neighbourhood(position):
return map(
lambda neighbour: EDGE if neighbour is None else position.absolute,
position.neighbours
)
Oops, I've accidentally written 'position.absolute' instead of 'neighbour.absolute'!
This version doesn't make any declaration like (1), so the computer can't help me find or fix the problem; it's a perfectly valid program. A static type checker like mypy wouldn't help me either, since 'position' and 'neighbour' are presumably both the same type.
It's not even clear to a human that there's anything wrong with this code. The problem would only arise during testing (we hope!), and the logic error would have to be manually narrowed-down to this function. Even if we correctly diagnose that the 'if' is returning a different variable than it was checking, the fix is still ambiguous. We could do this:
EDGE if position is None else position.absolute
Or this:
EDGE if neighbour is None else neighbour.absolute
Both are consistent, but only the second one matches the shadowing example.
> Oops, I've accidentally written 'position.absolute' instead of 'neighbour.absolute'!
I'm going to be honest here, the number of times I've made that kind of mistake is absolutely dwarfed by the number of times I have used the wrong variable because I had accidentally shadowed it.
Neither mistake is super common, but I can't recall ever writing 'position.absolute' instead of 'neighbour.absolute' unless I legitimately needed both position and neighbour in scope and the problem was hard to reason about. I can recall accidentally reusing a variable like 'x' as an iteration variable and then using the wrong 'x' because I forgot, and I can also recall misunderstanding what some piece of code did because I thought 'x' was referring to the outer scope but I had missed that it was shadowed by another declaration. Shadowing has caused me many more problems than it solved, at least in my own experience.
>Oops, I've accidentally written 'position.absolute' instead of 'neighbour.absolute'!
That's a contrived example though, if I ever saw one.
I don't think that's the kind of issue people commonly have, compared to misuse of shadowed variable further down the scope.
And for your example, a better solution would be for the close to declare what it wants to use from its environment. Python doesn't allow this syntax, but some languages do:
def neighbourhood(position):
return map(
lambda neighbour use (): EDGE if neighbour is None else position.absolute,
position.neighbours
)
Now the compiler can again warn you, since you're only allowed to use neighbour in the lambda.
You know, a lot of potentially confusing behavior would be avoided if programming languages had the sense to make variables read-only by default and disallow variable shadowing altogether.