Skip to content

SPy Builtins

SPy adds several new builtins to the global namespace. Currently, all of them happen to be decorators for classes or callables.

Warning

The contents of the __spy__ module and SPy's builtins form the API surface of a language that's rapidly evolving. All of the constructs, names, functions, or decorators here are likely to change!

Important

For a deeper explanation of the current state of SPy's coloring nomenclature, see this post by Antonio Cuni.

Class and Callable Decorators

@struct

Used to create a struct from a class definition. See the low level memory section on structs for more details.

@blue

Declares that a function should be executed at redshift time; that is, at the point when method and function lookups are resolved, and prior to compilation to C or WASM, if any.
type annotations for @blue functions are optional. If omitted, the arguments and return types default to dynamic.
In this example, the two blue functions are evaluated during redshift time, and the emitted C code is just a print statement of a constant number.
@blue
def factorial(x)
    if x == 0: return 1
    if x == 1: return 1
    return x * factorial(x-1)

@blue
def calc_e(terms):
    sum: float = 0
    for i in range(terms):
        sum += 1/factorial(i)
    return sum

def main() -> None:
    print(calc_e(10))

@blue.generic

Functions decorated with @blue.generic are called using square brackets [] instead of parentheses (). Other than that, they are identical to other blue functions. While they may be used anywhere, the primary purposes is to allow the creation of 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'))

@blue.metafunc

Unlike blue.generic functions, metafuncs accept one or more arguments, and return an OpSpec appropriate to those arguments based on their static type.
  from operator import OpSpec

  @blue.metafunc
  def myprint(m_x):
      if m_x.static_type == int:
          def myprint_int(x: int) -> None:
              print(x)
          return OpSpec(myprint_int)

      if m_x.static_type == str:
          def myprint_str(x: str) -> None:
              print(x)
          return OpSpec(myprint_str)

      raise TypeError("don't know how to print this")

  def main() -> None:
      print(42)
      myprint("hello")
      myprint(5.2)  # raises TypeError

@force_inline

Causes the decorated function to be inlined during redshifting.
Only functions with a single return statement at the end of the function can be forced inline. Blue functions cannot be forced inline, nor can forced-inline statements be used recursively.

#inline_demo.spy
from __spy__ import force_inline

@force_inline
def inc(x: i32) -> i32:
    return x + 1

def main() -> None:
    print(inc(1))
# spy rs inline_demo.spy
def main() -> None:
    `_print::_print_one[i32]::impl`(__block__(x$0: i32 = 1; x$0 + 1))