Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the type of sum?

I want to express that the first parameter is a "list" of the second parameter, and that the result has the same type as the second parameter.

This mysum (ie. not the standard lib sum) should work equally well with int/float/str/list, and any other type that supports +=.

Naively:

def mysum(lst: list[T], start: T) -> T:
    x = start
    for item in lst:
        x += item
    return x    

which produces:

(dev311) go|c:\srv\tmp> mypy sumtype.py
sumtype.py:26: error: Unsupported left operand type for + ("T")
sumtype.py:35: error: Cannot infer type argument 1 of "mysum"
Found 2 errors in 1 file (checked 1 source file)

Second attempt:

from typing import Iterable, Protocol, TypeVar
T = TypeVar('T')

class Addable(Protocol[T]):
    def __add__(self, other: T) -> T:
        ...

class RAddable(Protocol[T]):
    def __radd__(self, other: T) -> T:
        ...

def mysum(lst: Iterable[Addable|RAddable], start: Addable) -> Addable:
    x = start
    for item in lst:
        x += item
    return x

however, this doesn't place any restrictions on the "list" items and start being the same type, so this typechecks:

class Foo:
    def __radd__(self, other: int) -> int:
        return other + 42


mysum([Foo()], [])  # <=== should ideally fail since Foo() and [] are not the same type

and fails with a type-error at runtime:

(dev311) go|c:\srv\tmp> python sumtype.py
Traceback (most recent call last):
  File "c:\srv\tmp\sumtype.py", line 27, in <module>
    mysum([Foo()], [])
  File "c:\srv\tmp\sumtype.py", line 18, in mysum
    x += item
  File "c:\srv\tmp\sumtype.py", line 24, in __radd__
    return other + 42
           ~~~~~~^~~~
TypeError: can only concatenate list (not "int") to list

I'm assuming it can't be this difficult to type-annotate a generic function, so what am I missing..?

like image 783
thebjorn Avatar asked Oct 16 '25 10:10

thebjorn


1 Answers

You can make a generic type bound to a protocol that supports __add__ instead:

Type variables can be bound to concrete types, abstract types (ABCs or protocols), and even unions of types

from typing import TypeVar, Protocol

T = TypeVar('T', bound='Addable')

class Addable(Protocol):
    def __add__(self: T, other: T) -> T:
        ...

def mysum(lst: list[T], start: T) -> T:
    x = start
    for item in lst:
        x += item
    return x

Demo: https://mypy-play.net/?gist=ecf4031f21fb81e75d6e6f75d16f3911

like image 57
blhsing Avatar answered Oct 19 '25 01:10

blhsing



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!