Skip to content

Sum of an iterable of bools is considered to have a bool type #7579

Closed

Description

@lancelote

Consider the following code

foo = sum([True, False])
foo += 1
  • mypy v0.931 is OK with it and reports foo type as int
  • mypy v0.940 warns
Incompatible types in assignment (expression has type "int", variable has type "Union[bool, Literal[0]]")

The stub does indeed state

@overload
def sum(__iterable: Iterable[_SumT]) -> _SumT | Literal[0]: ...

Though it is somewhat unfortunate. I've got beaten by this after updating mypy to the latest version.

Activity

  1. JelleZijlstra commented on Apr 2, 2022

    @JelleZijlstra
    Member

    See #7578 where we recently experimented with ways to address this sort of thing.

  2. ktbarrett commented on Apr 2, 2022

    @ktbarrett
    Contributor

    @JelleZijlstra I'm not sure how that's relevant. The problem is that bool + bool is int not bool. I think the only way to fix this is to add an overload for bool to return int.

  3. JelleZijlstra commented on Apr 2, 2022

    @JelleZijlstra
    Member

    @ktbarrett it's relevant because currently we assume that sum(Iterable[T]) returns T. We tried an alternative where we made sum return the return type of __add__ instead, but backed it out because it led to false positives.

    Type checkers already understand that bool + bool returns an int.

  4. ktbarrett commented on Apr 2, 2022

    @ktbarrett
    Contributor

    Ah, I see the middle commit now. No one seemed to comment on the failure.

  5. jpy-git commented on Apr 2, 2022

    @jpy-git
    Contributor

    I think the reason this was passing in mypy 0.931 was that the return used to be typed as _T | int and has since been changed to _T | Literal[0] (the typevar change in #7578 won't have made it into mypy yet)
    https://github.com/python/mypy/blob/4486d764c07a1cee9792d691e8e85cedfed2a451/mypy/typeshed/stdlib/builtins.pyi#L1389-L1393

    That int in the return type would have been allowing foo += 1 but now it's been changed to Literal[0] (since I think it's intended to represent the default start value).

    You can see an example here: https://mypy-play.net/?mypy=0.941&python=3.10&flags=strict&gist=4b96aaf8e1261db4bf484c0f76138638

    pyright doesn't raise this error on the newer stubs so maybe mypy needs to be updated to account for this as well?
    Alternatively, if the Literal[0] isn't providing any significant benefit, maybe it would just be simplest to revert Literal[0] to int?

  6. AlexWaygood commented on Apr 2, 2022

    @AlexWaygood
    Member

    since I think it's intended to represent the default start value

    Not quite. Literal[0] is there to represent the special case of the empty iterable being passed to sum.

    >>> x: list[list[int]] = []
    >>> sum(x)
    0
  7. jpy-git commented on Apr 2, 2022

    @jpy-git
    Contributor

    yes because if you have an empty iterable you never add anything to the start value which is by default 0 😄

    Help on built-in function sum in module builtins:
    
    sum(iterable, /, start=0)
        Return the sum of a 'start' value (default: 0) plus an iterable of numbers
    
  8. AlexWaygood commented on Apr 2, 2022

    @AlexWaygood
    Member

    yes because if you have an empty iterable you never add anything to the start value which is by default 0 😄

    Sorry, looks like we were saying the same thing in different ways there 😄

  9. AlexWaygood commented on Apr 2, 2022

    @AlexWaygood
    Member

    pyright doesn't raise this error on the newer stubs so maybe mypy needs to be updated to account for this as well?

    Yeah, there's some unfortunate disagreement among type checkers at the moment on how to treat literal types 😕 see #7258 for an extended discussion

    But while the immediate cause of the new error might have been the change from int to Literal[0], I don't think that's really the underlying problem here.

  10. AlexWaygood commented on May 28, 2022

    @AlexWaygood
    Member

    The specific case of bool + bool -> int has been fixed in #7975. The general case can't be solved without introducing a significant number of false-positive errors, as we saw when experimenting in #7578. As such, I'm closing this issue as as-fixed-as-it-can-be-for-now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions