Functions and Generics

These examples cover higher-level function features that are already usable in the H-Series subset.

Function overloading

Source file: guppy_examples/guppy-features/supported/function-overloading.py

import sys
from typing import no_type_check

from guppylang import guppy, qubit
from guppylang.std.platform import result
from guppylang.std.quantum import measure, x


@guppy
def one_state() -> qubit:
    q = qubit()
    x(q)
    return q


@guppy
def apply_x(q: qubit) -> None:
    x(q)


@guppy.overload(one_state, apply_x)
@no_type_check
def apply_x_to_something(): ...  # noqa: ANN201


@guppy
@no_type_check
def main() -> None:
    q = qubit()

    # compiler dispatches apply_x() to be used here
    apply_x_to_something(q)

    # compiler dispatches one_state() to be used here
    other_q = apply_x_to_something()

    result("q", measure(q))
    result("other_q", measure(other_q))


if __name__ == "__main__":
    sys.stdout.buffer.write(main.compile().to_bytes())

First-class functions

Source file: guppy_examples/guppy-features/supported/first-class-functions.py

import sys

from guppylang import guppy, qubit
from guppylang.std.builtins import result
from guppylang.std.quantum import measure, x


@guppy
def a_function(n: int) -> int:
    return n + 1


@guppy
def b_function() -> qubit:
    q1, q2 = qubit(), qubit()
    x(q1)
    if measure(q1):
        x(q2)
    return q2


@guppy
def main() -> None:
    # bind a variable to our function
    q_func = b_function
    my_function = a_function
    res = my_function(100)
    result("res", res)
    result("q", measure(q_func()))


if __name__ == "__main__":
    sys.stdout.buffer.write(main.compile().to_bytes())

Higher-order functions

Source file: guppy_examples/guppy-features/supported/higher-order-functions.py

import sys
from collections.abc import Callable

from guppylang import guppy
from guppylang.std.platform import result


@guppy
def my_function(f: Callable[[int], bool]) -> Callable[[int], bool]:
    # Takes a callable `f` that accepts an integer and returns a boolean.
    return f


@guppy
def main() -> None:
    def is_even(n: int) -> bool:
        return n % 2 == 0

    # # Apply our higher order function `my_function` to `is_even`
    my_function_composition = my_function(is_even)

    res = my_function_composition(42)
    result("res", res)


if __name__ == "__main__":
    sys.stdout.buffer.write(main.compile().to_bytes())

Generics

Source file: guppy_examples/guppy-features/supported/generics.py

from typing import no_type_check

from guppylang import guppy, qubit
from guppylang.std.builtins import array, result
from guppylang.std.lang import owned
from guppylang.std.quantum import discard, measure

T = guppy.type_var("T", copyable=False, droppable=False)
n = guppy.nat_var("n")


@guppy
@no_type_check
def identity(me: T @ owned) -> T:
    return me


@no_type_check
def apply_identity(a: array) -> array:
    """Since we are passing in an array, this must be pure python
    that we call from guppy.comptime.

    However, we can still call a guppy function from here, even a
    generic one
    """
    ret = array()
    for i in range(len(a)):
        ret.append(identity(a[i]))
    return ret


@guppy.comptime
@no_type_check
def main() -> None:
    arr1 = array(1, 2)
    arr1 = apply_identity(arr1)

    arr2 = array(1.5, 2.5, 3.5, 4.5)
    arr2 = apply_identity(arr2)

    arr3 = array(qubit() for _ in range(8))
    arr3 = apply_identity(arr3)

    for i in range(7):
        discard(arr3[i])

    result("arr1_1", arr1[1])
    result("arr3_7", measure(arr3[7]))

The generic array example is still constrained by the same array rules described in Arrays: it works here because the array manipulation happens from Python under @guppy.comptime.