draft: Generic class type alias#529
Conversation
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude <claude@anthropic.com>
…ution Co-authored-by: Claude <claude@anthropic.com>
…ssDef Co-authored-by: Claude <claude@anthropic.com>
205899c to
4813eb8
Compare
|
Just wanting to understand this a little bit - does this unblock new capabilities within generics, or is it a syntactic thing to write clearer code? Having types as an alias only work within generic functions is just a bit confusing for me... |
|
@paugier I don't understand how E.g., the following seems to work fine: @struct
class Foo[T]:
TUP = tuple[T, T]
def bar(self) -> TUP:
return 1, 2
def main() -> None:
f = Foo[int]()
print(f.bar()) |
|
This PR is just syntactic sugar (just avoiding a lot of repetitions of potentially relatively long concrete types, typically from unsafe import gc_alloc, gc_ptr
@struct
class ArrayData[DTYPE]:
length: i32
capacity: i32
items: gc_ptr[DTYPE]
@struct
class Array1d[DTYPE]:
type ArrData = ArrayData[DTYPE]
__ll__: gc_ptr[ArrData]
def __new__(length: i32) -> Self:
ll = gc_alloc[ArrData](1)
ll.length = length
ll.capacity = length
ll.items = gc_alloc[DTYPE](length)
return Self.__make__(ll)
def main() -> None:
a_floats = Array1d[f64](10)
print(a_floats)Without the @blue.generic
def Array1d(DTYPE):
@struct
class Self:
# this is a class attribute (not what we want)
ArrData = ArrayData[DTYPE]
__ll__: gc_ptr[ArrData]
def __new__(length: i32) -> Self:
ll = gc_alloc[ArrData](1)
ll.length = length
ll.capacity = length
ll.items = gc_alloc[DTYPE](length)
return Self.__make__(ll)
return SelfWith the @blue.generic
def Array1d(DTYPE):
# just a local type alias
ArrData = ArrayData[DTYPE]
@struct
class Self:
__ll__: gc_ptr[ArrData]
def __new__(length: i32) -> Self:
ll = gc_alloc[ArrData](1)
ll.length = length
ll.capacity = length
ll.items = gc_alloc[DTYPE](length)
return Self.__make__(ll)
return SelfFor generic types defined with the syntactic sugar I think the Python I think this syntax is only useful in generic structs written with the |
|
An example of "real" code for which I wish to use that: https://github.com/paugier/spy-demos/blob/loop-fusion/loop-fusion/lib_ndarray.spy#L182. Note @blue.generic
def LazyAdd(DTYPE, NDIM, LEFT_T, RIGHT_T):
# a type alias
A = ndarray[DTYPE, NDIM]
@struct
class Self:
# class attributes
_DTYPE = DTYPE
_NDIM = NDIM
left: LEFT_T
right: RIGHT_Tand the numbers of |
I think it wrongly fails :). There WILL be a difference with python because things like I'm also not sure that the # fictional example
class FixedArray[T, N]:
bytes_size = T.sizeof() * N
buf: char_array[bytes_size]this is exactly the same situation as yours, but Also, don't forget that the Anyway, for the specific case at hand, I think that it should just work out of the box. |
|
I agree that
In Python, you cannot use a class variable in the body of a method. Typically, this fails with "NameError: name 'ArrData' is not defined. Did you mean: 'self.ArrData'?": class ArrayData[DTYPE]:
length: int
capacity: int
items: list[DTYPE]
class Array1d[DTYPE]:
ArrData = ArrayData[DTYPE]
def foo(self) -> None:
print(ArrData)Do you confirm that you like this to be supported in SPy? This would be convenient but it is a big change compared to Python (which needs It would make sense to only support |
ah ok, I see what you mean. That's one of the mane python scoping weirdness. class MyList[T]:
type DATA = list[T]
def foo(self) -> DATA:
x: DATA
return xThis fails with mypy 2.1.0; I'm undecided between various conflicting solutions here. (non) solution 1To declare that we do as in CPython and this pattern just doesn't work. solution 2Abuse the class Array1d[DTYPE]:
nonlocal ArrData
ArrData = ArrayData[DTYPE]
def foo(self) -> None:
print(ArrData)Unfortunately solution 3similar to (2), but we modify the parser and we add another modifier similar to what we do already for class Array1d[DTYPE]:
outer ArrData = ArrayData[DTYPE]
def foo(self) -> None:
print(ArrData)solution 4declare that class scopes work very differently than CPython:
This is the bigger departure from CPython semantics, but at the same time it might be the cleanest/closest to existing mypy semantics. But before deciding on this we would need to think deeper because it might interact badly with other cases. |
|
My personal preference goes to abusing
Generally, it seems to me that there should be very good reasons to decide to depart from Python semantics. It seems to me that Solution 4 is a big departure compared to how Python and Mypy work, which is not going to be natural for most Python developers. And I don't see the strong advantage of this solution. Note that currently, blue class variables do not need to be explicitly typed: @blue.generic
def LazyAdd(DTYPE, NDIM, LEFT_T, RIGHT_T):
A = ndarray[DTYPE, NDIM]
@struct
class Self:
# not explicitly typed
_DTYPE = DTYPE
_NDIM = NDIM |
For my loop fusion demo, I see that we need a syntax compatible with generic struct for
I propose to support
(just syntactic sugar for the former). This PR is an implementation of that. Good shape but not yet ready for review.
Done with Claude under my supervision.