Conversation
…ation Map is a new operator that wraps the comprehension and will eventually support different semantics (e.g. propagating Any). For now it behaves identically to the bare list comprehension. Mypy tests for files that use Map are xfailed until the stubs are updated.
Iter[Any] now returns a generator that raises IterAnyError on first __next__ (not __iter__, which CPython calls eagerly when constructing a genexpr -- before Map can wrap it). Map catches IterAnyError via `yield from` and yields a _UnpackAny sentinel, which callers of _eval_args short-circuit into typing.Any.
Map.__iter__ now just yields an (_UnpackedMap, _UnpackedMapEnd) pair; the evaluator drives the wrapped generator inside _eval_args, catches IterAnyError there, and emits _UnpackAny. The trailing sentinel keeps Union[*Map(...)] from collapsing to a single arg before _eval_union can see it. IterAnyError is now a TypeMapError subclass, living entirely inside the evaluator. _eval_args iterates the generator one value at a time so that nested genexprs closing over the outer iteration variable don't all see its final value. _eval_union now routes through _eval_args too.
Both the _UnpackedMap branch and the plain-arg branch did the same eval-and-unpack dance; pull it out.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Question: why exactly is this better? Does Related, I think it would be nice if we could write As an example, I had the following decorator: def computed[*T, V](
*reactives: *[signal[t] for t in Iter[tuple[*T]]],
name: str | None = None,
) -> Callable[[Callable[[*T], V]], signal[V]]: ... |
Yes, the idea here is that we would like to be able to turn something like By using That said, I don't think we are going to go with this approach.
I will ponder this; it might be doable. Did that *reactives type work? |
|
Thank you! AST evaluation sounds nice. The from collections.abc import Callable
from dataclasses import dataclass
from typemap_extensions import Iter
@dataclass
class signal[T]:
value: T | None
def computed[*T, V](
*reactives: *[signal[t] for t in Iter[tuple[*T]]],
) -> Callable[[Callable[[*T], V]], signal[V]]:
result = signal[V](None)
def decorator(func: Callable[[*T], V]) -> signal[V]:
# Not relevant right now. In here, we'd automatically update the result
# signal, based on the input signals.
return result
return decorator
r1 = signal[int](1)
r2 = signal[int](2)
@computed(r1, r2)
def f1(value1: int, value2: int) -> int:
# produce a new signal that's the sum of the given signals.
return value1 + value2
@computed(r1, r2)
def f2(value1: str, value2: str) -> str:
# Mypy should fail here! Signals are of type int, not str.
return value1 + value2 |
This lets us turn maps over
Iter[Any]intoAny, which I think wereally might want to do.