python / python/mypy

Implicit TypeVar expansion of a Generic via another generic method

オープン
#6,933 コメント 7 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

bug priority-1-normal topic-type-variables
主要言語
Python
スター
20.6k
フォーク
3.3k
PR マージ指標
PR 指標を取得中

説明

I really struggled to come up with a title for this, so I apologize for the ambiguity. I also considered "Spooky TypeVar action at a distance".

I'm investigating strategies for static type checking of apache beam, which is a streaming pipeline API.

Basically, I have a generic Transform with an In and Out type, and another generic Collection that wants to apply its type to the input type of Transform in order to produce a new Collection with the Transform's output type. These operations are repeated in a chain to form a pipeline:

Collection[None] -> Transform[None, A] -> Collection[A] -> Transform[A, B] -> Collection[B]

The catch is that a Transform's output type can be related to its input type via a TypeVar, and a Collection contains the concrete type of that input TypeVar. So when we call Collection.apply(transform) we want to resolve that chain of TypeVar dependencies to produce an expanded/concrete output type:

from typing import *

T = TypeVar('T')
InT = TypeVar('InT')
OutT = TypeVar('OutT')


class Transform(Generic[InT, OutT]):
    "Takes an input Collection and produces an output Collection"

    def __init__(self, fn: Callable[[InT], OutT]):
        self.fn = fn

    def call(self, arg: InT) -> OutT:
        return self.fn(arg)


def get_op(f: Callable[[InT], OutT]) -> Transform[InT, OutT]:
    "Get a Transform from a callable"
    return Transform(f)


class Collection(Generic[T]):
    "Collection of elements"

    def apply(self, op: Transform[T, OutT]) -> 'Collection[OutT]':
        """
        Apply the this Collection to a Transform and produce a new 
        output Collection
        """


# -- test:

def make_string(x: None) -> str:
    return 'foo'


def make_ones(x: T) -> Tuple[T, int]:
    return (x, 1)


c1: Collection = Collection()

str_op = get_op(make_string)
reveal_type(str_op)  # revealed: Transform[None, builtins.str*]

c2 = p1.apply(str_op)
reveal_type(c2)  # revealed: Collection[builtins.str*]

tuple_op = get_op(make_ones)
reveal_type(tuple_op)  # revealed: Transform[T`-1, Tuple[T`-1, builtins.int]]

c3 = c2.apply(tuple_op)
# Desired type is Collection[Tuple[builtins.str, builtins.int]]
reveal_type(c3)  # revealed: Collection[Tuple[T`-1, builtins.int]]

The crux of the issue is here:

class Collection(Generic[T]):
    "Collection of elements"

    def apply(self, op: Transform[T, OutT]) -> 'Collection[OutT]':
        """
        Apply the this Collection to a Transform and produce a new 
        output Collection
        """

My naive desire was that my Collection[builtins.str*] would apply its str type to T-1 of Transform[T-1, Tuple[T-1, builtins.int]] to produce Collection[Tuple[builtins.str, builtins.int]].

Instead I get:

error: Argument 1 to "apply" of "Collection" has incompatible type "Transform[T, Tuple[T, int]]"; expected "Transform[str, Tuple[T, int]]"

Is there any future universe where this is possible in mypy, or is it just too ambiguous?

Is it possible to write a mypy plugin to produce the desired result for Collection.apply?

  • edit: clarified relationship between Transform and Collection TypeVars

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

まず、提供された汎用的な Transform/Collection の例を mypy で再現し、Collection.apply とその reveal_type の結果に焦点を当てます。チェッカーが TypeVar 間の関係をどのように処理するかを追跡し、plugin のエントリーポイントでこれをサポートできるかを評価します。完了とは、実装方針を明確に限定して確立すること、または要求された推論がサポートされない理由を文書化することを意味します。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
python
領域
devtools
issue の種類
機能追加
難易度
5/5
見積もり時間
1週間以上
活発さ
停滞
明瞭さ
説明が足りない
初心者へのやさしさ
35/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。