Skip to content

Types as function namespaces #5980

Closed
Closed

Description

@jesaerys

I often wish that types could also be namespaces for functions. To motivate this idea, let me start with a simple example. Say I need some functions that apply to arbitrary 2-tuples. The simplest solution is to write a module defining a Pair type along with the functions I need:

# pair.py
from typing import Tuple, TypeVar

A = TypeVar('A')
B = TypeVar('B')

Pair = Tuple[A, B]

def fst(pair: Pair[A, B]) -> A:
    a, _ = pair
    return a

def snd(pair: Pair[A, B]) -> B:
    _, b = pair
    return b

I would use my module like this:

# test.py
import pair
from pair import Pair

def f(p: Pair[int, float]) -> float:
    return pair.fst(p) + pair.snd(p)

p: Pair[int, float] = (1, 1.5)
assert f(p) == 2.5

This works and it typechecks with mypy. However, naming is a bit cumbersome with this approach. I can't use the pair identifier for Pair variables because I've already used it for my module, and I'm forced to resort to something like p instead. Also, it would be nicer if the functions fst and snd had a clearer association with the type Pair.

I'd much rather write something like this for my test:

# test.py
from pair import Pair

def f(pair: Pair[int, float]) -> float:
    return Pair.fst(pair) + Pair.snd(pair)

pair: Pair[int, float] = (1, 1.5)
assert f(pair) == 2.5

where Pair is now both a type and a namespace for associated functions.

Is it possible for mypy to support this?

I'm not making any syntax proposals here, but I'll note that the following does work at runtime:

from typing import Tuple, TypeVar

A = TypeVar('A')
B = TypeVar('B')

class Pair(Tuple[A, B]):

    def fst(pair: Pair[A, B]) -> A:
        a, _ = pair
        return a

    def snd(pair: Pair[A, B]) -> B:
        _, b = pair
        return b


def f(pair: Pair[int, float]) -> float:
    return Pair.fst(pair) + Pair.snd(pair)

pair: Pair[int, float] = (1, 1.5)
assert f(pair) == 2.5

Unfortunately, it does not typecheck with mypy, first of all because generic tuples are not allowed (#685). Second, even if generic tuples were supported, my assignment statement for pair would fail with an "Incompatible type" error because the expression has type Tuple[int, float] while the variable has type Pair[int, float], and mypy sees Pair as a subtype of Tuple due to the class definition.

I'm really curious to hear people's thoughts on this.

Thanks.

Activity

  1. hauntsaninja commented on Aug 25, 2022

    @hauntsaninja
    Collaborator

    Use staticmethod to use a class as a namespace.

  2. jesaerys commented on Aug 29, 2022

    @jesaerys
    Author

    I completely lost track of this. Apparently I was looking for a way to associate methods/functions with a type without creating a subclass, but it's been a long time since I opened this issue and I can't remember why. As far as I can tell, this still isn't really possible and my first example (type alias + independent functions) is the most straightforward workaround.

    That aside, I'm happy to see that subclassing generic tuples now typechecks on master, I assume as of #13396. Full example:

    from __future__ import annotations
    
    from typing import Tuple, TypeVar
    
    A = TypeVar('A')
    B = TypeVar('B')
    
    class Pair(Tuple[A, B]):
    
        @staticmethod
        def fst(pair: Pair[A, B]) -> A:
            a, _ = pair
            return a
    
        @staticmethod
        def snd(pair: Pair[A, B]) -> B:
            _, b = pair
            return b
    
    
    def f(pair: Pair[int, float]) -> float:
        return Pair.fst(pair) + Pair.snd(pair)
    
    pair: Pair[int, float] = Pair((1, 1.5))
    assert f(pair) == 2.5

    (Thanks to @hauntsaninja for reminding me that fst and snd should be decorated with @staticmethod.)

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