Any chance for reconsideration of PEP 335?

def simple_calc(...) -> T | None:
    """Returns None if the trivial simplification for foo-algo doesn't apply"""
     ...

def expensive_calc(...) -> T:
    """Implements the slow, but always possible method"""
    ...


simple_calc(...) or expensive_calc(...)

The intent of this code would be broken by the type T gaining something that changes the semantic behavior of or, and the type T could be some type the author of this code doesn’t control.

Changing the behavior of or and and will break code, how much is difficult to say, and it would basically be a breaking change to every possible user of any type that added this behavior.

1 Like

In other words the code could be broken by someone changing some code to make use of the new feature.

There is a big difference between saying that existing code (unchanged) would be broken and saying that new code using the new feature might not work with some existing code. That is the distinction I wanted to make.

“We” by approving the existence of this new feature do not break any existing unchanged code. If the feature then exists people would need to decided how to use it and changing existing types to use it would potentially break some code but that is generally true of any change to existing types.

1 Like

I think you’re misunderstanding the scope here. This breaks the pattern x() or y() because it’s unsafe to use if the language no longer guarantees this short-circuits. You know have to care if people add functionality, as that becomes breaking.

3 Likes

No, it could be broken by changing some OTHER code a long way away. That is very different from changing the actual code that got broken.

4 Likes

I am not misunderstanding the scope. I made a very clear point about what it means to “break code”.

I agree with your point about this sort of behaviour being problematic but it has no bearing on whether existing unchanged code would be broken by this change.

1 Like

For example, if type T raises an exception from __bool__, like numpy.ndarray, or the new proposed sentinel objects, or like existing sentinel objects from third party library, or sympy equations, or … Why are you guys ignoring the fact that this code already can be easily broken and in fact is broken for many potential objects?

How is a type overwriting __bool__ different from a type overwriting __and1__?

4 Likes

The unchanged code would be broken by the ability to change the behavior of or, and as I said, it could be out of your control and not something you would design for since the language says this short circuits.

If you’re not misunderstanding the scope, then I’m not sure you’ve read what I said.

The behaviour of or is only changed by changing the code.

Well, for one thing, I consider what numpy does here to be a blatant misuse, but I also find erroring all the time in a noticeable way much more tolerable than a bunch of accidental performance regressions that the user may not have an obvious sign of the cause.

By changing the type of T to add functionality, not the code here. It makes an Addition of functionality breaking to all possible users of the pattern.

One of them applies to every single place where the truthiness is used. The other applies to just one place - or maybe more, if a < b < c also triggers it - and disconnects things that were previously equivalent.

I don’t think there’s any point in my discussing this further with you. You are free to spend your time on this, but you’re not getting anywhere with convincing people of the merits of your arguments.

This is true of any change to a type. Adding the (optional) feature that makes it possible to effect how or is handled for a type does not in itself break existing code. If a type is changed to use this feature then that might break existing code. That is very different though from saying that the mere existence of the feature that or can be overloaded breaks the code. Let me requote what I first said in this thread and what I was responding to:

You’re still not getting it. This takes the existing idiomatic pattern that exists because of language guarantees about short circuiting x or y and says the pattern isn’t reliable.

Types that error out instead of Returning NotImplemented for truthiness shouldn’t do that either, but at least that will result in traceback and someone will fix it. Static analysis catches that right now too by marking code under it as unreachable. It won’t be able to catch that x() and y() had a behavior change, and could result not just in double computation, but in the wrong outcome, if T and T results in a new T.

That’s language level breaking when you break idiomatic patterns of the language IMO.

2 Likes

Just because you said not to say it, doesn’t make it irrelevant. :slight_smile:

I understand that. You are not getting the very simple point that I made:

This feature does not in itself break existing code (at least as I understand it).

That does not mean that I support the feature. It also does not mean that I cannot see how it could lead to problems in future.

I can see why @MegaIng wanted to bow out from the discussion if trying to clarify a basic aspect of the proposal leads to being hounded like this by people wanting to shout the proposal down rather than discuss it.

2 Likes

Well, you say it isn’t breaking. I say it breaks the current language guarantees that people have written code based on, and that through decades of collective use, the community has used in ways that people would consider idiomatic.

I think it’s frankly irrelevent if it requires someone to change a type to change the behavior of or on that type, because

  1. They may not control that type. My example is general enough to show that the person writing the function returning the type and writing or could be broken without the type being theirs.
  2. Breaking the language guarantees about this breaks the language level concept people have been writing code with a reliance on.
1 Like

If you use a type that you don’t control and the people who do control it change the type then there are many ways that they could break you by making changes to the type.

At a language level if “we” add a feature that enables types to do do something on an opt-in basis that does not break your code that uses the type. If the type opts in and the behaviour breaks your code then that breaks your code. The authors in control of the type need to be aware of this regardless if whether they are using new language features or not.

1 Like

I fundamentally disagree about who is responsible for that breakage when it is done to an operator that currently has short-circuiting, isn’t overridable, and where we know people rely upon that. I notice you didnt even attempt to address that portion.

1 Like

Because he doesn’t argue with that, he just tries to clarify what he has said, because you instead of saying “ok, I understand, but there is another issue”, go arguing that he was wrong in his simple and clear statement - “This would not break existing code as it is”.

I said above that I can see how this could be problematic.

You are still arguing with me about a point that you think I am making rather than the point that I actually made: this proposal does not break existing unchanged code.

1 Like