Generic Functions and Types
Generic Functions¶
Functions decorated with @blue.generic are called with [] brackets instead of parentheses. These may be used anywhere, but they are intended to help create functions that look like PEP 695 functions with type parameters:
@blue.generic
def add(T):
def impl(x:T, y: T) -> T:
return x + y
return impl
def main() -> None:
print(add[i32](1, 2))
print(add[str]('hello ', 'world'))
Like all functions marked @blue, the generic function is guaranteed to be executed at compile-time. We can see in the redshifted version of the above code that add() no longer appears, but the two specialized versions of it remain:
def main() -> None: `_print::println[i32]::p`(`t::add[i32]::impl`(1, 2)) `_print::println[str]::p`(`t::add[str]::impl`('hello ', 'world')) def `t::add[i32]::impl`(x: i32, y: i32) -> i32: return x + y def `t::add[str]::impl`(x: str, y: str) -> str: return `operator::str_add`(x, y)
Generic Class Syntax¶
@struct classes may also be created with one or more parameters in [] brackets. This is different from passing superclasses inside of () parentheses; rather, this is syntactic sugar for a generic function with an inner @struct class than can make use of those parameters:
@struct
class MyList[T]:
inner: list[T]
other_param_1: ...
other_param_2: ...
# ↑ is syntactic sugar for ↓
@blue.generic
def MyList(T):
@struct
class Self:
inner: list[T]
other_param_1: ...
other_param_2: ...
return Self
In use, this looks like:
@struct
class MyNamedList[T]:
name: str
data: list[T]
def main() -> None:
my_int_list = MyNamedList[i32]("profits", [])
my_int_list.data.append(1_000_000)
my_str_list = MyNamedList[str]("words", ["hello", "world"])
my_str_list.data.extend(["and", "goodbye"])
For a larger example, see the myarray example on GitHub or run it in the SPy Playground
__origin__¶
The __origin__ attribute of SPy objects carries information about the generic function which created them, if any. When blue.generic defines a type or function and returns it, the returned object has it's __origin__ is set to the generic function:
@blue.generic
def adder(T):
@struct
class impl:
...
return impl
def main() -> None:
assert adder[T].__origin__ is adder
This is a straightforward way to identify that, for example, MyList[T] is a 'specialised' version of MyList on the type T.
(The default value for __origin__ is None. If the object returned by a generic function already has a non-None origin, that origin will not be overwritten.)
The __origin__ functions identically with the Generic Class syntax described above: