diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index d3658dd06..852e93029 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -1,4 +1,4 @@ -Contributor Guidelines +Contributor guidelines ====================== Contributions are welcome and greatly appreciated. Every little bit diff --git a/docs/api.rst b/docs/api.rst index e14858817..2cbcb7b1d 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1,392 +1,341 @@ API === +This chapter describes most of Hy's public-facing macros, functions, and +classes. It refers to Python's own documentation when appropriate +rather than recapitulating the details of Python semantics. + .. contents:: Contents :local: .. _core-macros: -Core Macros +Core macros ----------- The following macros are automatically imported into all Hy modules as their base names, such that ``hy.core.macros.foo`` can be called as just ``foo``. +Macros that are also available as functions are described as functions +under :ref:`pyop`. -.. hy:macro:: (annotate [value type]) +Fundamentals +~~~~~~~~~~~~ - ``annotate`` and its shorthand form ``#^`` are used to denote annotations, - including type hints, in three different contexts: +.. hy:macro:: (do [#* body]) - - Standalone variable annotations (:pep:`526`) - - Variable annotations in a :hy:func:`setv` call - - Function-parameter annotations (:pep:`3107`) + ``do`` (called ``progn`` in some Lisps) takes any number of forms, + evaluates them, and returns the value of the last one, or ``None`` if no + forms were provided. :: - The difference between ``annotate`` and ``#^`` is that ``annotate`` requires - parentheses and takes the name to be annotated first (like Python), whereas - ``#^`` doesn't require parentheses (it only applies to the next two forms) - and takes the type second:: + (+ 1 (do (setv x (+ 1 1)) x)) ; => 3 - (setv (annotate x int) 1) - (setv #^ int x 1) +.. hy:macro:: (do-mac [#* body]) - The order difference is not merely visual: ``#^`` actually evaluates the - type first. + ``do-mac`` evaluates its arguments (in order) at compile time, and leaves behind the value of the last argument (``None`` if no arguments were provided) as code to be run. The effect is similar to defining and then immediately calling a nullary macro, hence the name, which stands for "do macro". :: - Here are examples with ``#^`` for all the places you can use annotations:: + (do-mac `(setv ~(hy.models.Symbol (* "x" 5)) "foo")) + ; Expands to: (setv xxxxx "foo") + (print xxxxx) + ; => "foo" - ; Annotate the variable `x` as an `int` (equivalent to `x: int`). - #^ int x - ; You can annotate with expressions (equivalent to `y: f(x)`). - #^(f x) y + Contrast with :hy:func:`eval-and-compile`, which evaluates the same code at compile-time and run-time, instead of using the result of the compile-time run as code for run-time. ``do-mac`` is also similar to Common Lisp's SHARPSIGN DOT syntax (``#.``), from which it differs by evaluating at compile-time rather than read-time. - ; Annotations with an assignment: each annotation `(int, str)` - ; covers the term that immediately follows. - ; Equivalent to `x: int = 1; y = 2; z: str = 3` - (setv #^ int x 1 y 2 #^ str z 3) +.. hy:macro:: (eval-and-compile [#* body]) - ; Annotate `a` as an `int`, `c` as an `int`, and `b` as a `str`. - ; Equivalent to `def func(a: int, b: str = None, c: int = 1): ...` - (defn func [#^ int a #^ str [b None] #^ int [c 1]] ...) + ``eval-and-compile`` takes any number of forms as arguments. The input forms are evaluated as soon as the ``eval-and-compile`` form is compiled, then left in the program so they can be executed at run-time as usual; contrast with :hy:func:`eval-when-compile`. So, if you compile and immediately execute a program (as calling ``hy foo.hy`` does when ``foo.hy`` doesn't have an up-to-date byte-compiled version), ``eval-and-compile`` forms will be evaluated twice. For example, the following program :: - ; Function return annotations come before the function name (if - ; it exists). - (defn #^ int add1 [#^ int x] (+ x 1)) - (fn #^ int [#^ int y] (+ y 2)) + (eval-when-compile + (print "Compiling")) + (print "Running") + (eval-and-compile + (print "Hi")) - For annotating items with generic types, the :hy:func:`of ` - macro will likely be of use. + prints - An issue with type annotations is that, as of this writing, we know of no Python type-checker that can work with :py:mod:`ast` objects or bytecode files. They all need Python source text. So you'll have to translate your Hy with ``hy2py`` in order to actually check the types. + .. code-block:: text -.. _dot: + Compiling + Hi + Running + Hi -.. hy:data:: . + The return value of ``eval-and-compile`` is its final argument, as for :hy:func:`do`. - The dot macro ``.`` compiles to one or more :ref:`attribute references - `, which select an attribute of an object. The - first argument, which is required, can be an arbitrary form. With no further - arguments, ``.`` is a no-op. Additional symbol arguments are understood as a - chain of attributes, so ``(. foo bar)`` compiles to ``foo.bar``, and ``(. a b - c d)`` compiles to ``a.b.c.d``. + One possible use of ``eval-and-compile`` is to make a function available both at compile-time (so a macro can call it while expanding) and run-time (so it can be called like any other function):: - As a convenience, ``.`` supports two other kinds of arguments in place of a - plain attribute. A parenthesized expression is understood as a method call: - ``(. foo (bar a b))`` compiles to ``foo.bar(a, b)``. A bracketed form is - understood as a subscript: ``(. foo ["bar"])`` compiles to ``foo["bar"]``. - All these options can be mixed and matched in a single ``.`` call, so :: + (eval-and-compile + (defn add [x y] + (+ x y))) - (. a (b 1 2) c [d] [(e 3 4)]) + (defmacro m [x] + (add x 2)) - compiles to + (print (m 3)) ; prints 5 + (print (add 3 6)) ; prints 9 - .. code-block:: python + Had the ``defn`` not been wrapped in ``eval-and-compile``, ``m`` wouldn't be able to call ``add``, because when the compiler was expanding ``(m 3)``, ``add`` wouldn't exist yet. - a.b(1, 2).c[d][e(3, 4)] + While ``eval-and-compile`` executes the same code at both compile-time and run-time, bear in mind that the same code can have different meanings in the two contexts. Consider, for example, issues of scoping:: - :ref:`Dotted identifiers ` provide syntactic sugar for - common uses of this macro. In particular, syntax like ``foo.bar`` ends up - meaning the same thing in Hy as in Python. Also, :hy:func:`get - ` is another way to subscript in Hy. + (eval-when-compile + (print "Compiling")) + (print "Running") + (eval-and-compile + (setv x 1)) + (defn f [] + (setv x 2) + (eval-and-compile + (setv x 3)) + (print "local x =" x)) + (f) + (eval-and-compile + (print "global x =" x)) -.. hy:macro:: (fn [args]) + The form ``(setv x 3)`` above refers to the global ``x`` at compile-time, but the local ``x`` at run-time, so the result is: - As :hy:func:`defn`, but no name for the new function is required (or - allowed), and the newly created function object is returned. Decorators and - type parameters aren't allowed, either. However, the function body is - understood identically to that of :hy:func:`defn`, without any of the - restrictions of Python's :py:keyword:`lambda`. ``:async`` is also allowed. + .. code-block:: text -.. hy:macro:: (defn [name #* args]) + Compiling + global x = 3 + Running + local x = 3 + global x = 1 - ``defn`` compiles to a :ref:`function definition ` (or possibly - to an assignment of a :ref:`lambda expression `). It always - returns ``None``. It requires two arguments: a name (given as a symbol; see - :hy:func:`fn` for anonymous functions) and a "lambda list", or list of - parameters (also given as symbols). Any further arguments constitute the - body of the function:: +.. hy:macro:: (eval-when-compile [#* body]) - (defn name [params] bodyform1 bodyform2…) + ``eval-when-compile`` executes the given forms at compile-time, but discards them at run-time and simply returns :data:`None` instead; contrast :hy:func:`eval-and-compile`. Hence, while ``eval-when-compile`` doesn't directly contribute code to the final program, it can change Hy's state while compiling, as by defining a function:: - An empty body is implicitly ``(return None)``. If there are at least two body - forms, and the first of them is a string literal, this string becomes the - :term:`py:docstring` of the function. The final body form is implicitly - returned; thus, ``(defn f [] 5)`` is equivalent to ``(defn f [] (return - 5))``. There is one exception: due to Python limitations, no implicit return - is added if the function is an asynchronous generator (i.e., defined with - ``(defn :async …)`` or ``(fn :async …)`` and containing at least one - :hy:func:`yield`). + (eval-when-compile + (defn add [x y] + (+ x y))) - ``defn`` accepts a few more optional arguments: a literal keyword ``:async`` - (to create a :ref:`coroutine ` like Python's ``async def``), a - bracketed list of :term:`decorators `, a list of type - parameters (see below), and an annotation (see :hy:func:`annotate`) for the - return value. These are placed before the function name (in that order, if - several are present):: + (defmacro m [x] + (add x 2)) - (defn :async [decorator1 decorator2] :tp [T1 T2] #^ annotation name [params] …) + (print (m 3)) ; prints 5 + (print (add 3 6)) ; raises NameError: name 'add' is not defined - ``defn`` lambda lists support all the same features as Python parameter - lists and hence are complex in their full generality. The simplest case is a - (possibly empty) list of symbols, indicating that all parameters are - required, and can be set by position, as in ``(f value)``, or by name, as in - ``(f :argument value)``. To set a default value for a parameter, replace the - parameter with the bracketed list ``[pname value]``, where ``pname`` is the - parameter name as a symbol and ``value`` is an arbitrary form. Beware that, - per Python, ``value`` is evaluated when the function is defined, not when - it's called, and if the resulting object is mutated, all calls will see the - changes. +.. hy:macro:: (py [string]) - Further special lambda-list syntax includes: + ``py`` parses the given Python code at compile-time and inserts the result into + the generated abstract syntax tree. Thus, you can mix Python code into a Hy + program. Only a Python expression is allowed, not statements; use + :hy:func:`pys ` if you want to use Python statements. The value of the + expression is returned from the ``py`` form. :: - ``/`` - If the symbol ``/`` is given in place of a parameter, it means that all - the preceding parameters can only be set positionally. + (print "A result from Python:" (py "'hello' + 'world'")) - ``*`` - If the symbol ``*`` is given in place of a parameter, it means that all - the following parameters can only be set by name. + The code must be given as a single string literal, but you can still use + macros, :hy:func:`hy.eval `, and related tools to construct the ``py`` form. If + having to backslash-escape internal double quotes is getting you down, try a + :ref:`bracket string `. If you want to evaluate some Python + code that's only defined at run-time, try the standard Python function + :func:`eval`. - ``#* args`` - If the parameter list contains ``#* args`` or ``(unpack-iterable - args)``, then ``args`` is set to a tuple containing all otherwise - unmatched positional arguments. The name ``args`` is merely cherished - Python tradition; you can use any symbol. + The code is implicitly wrapped in parentheses so Python won't give you grief + about indentation. After all, Python's indentation rules are only useful for + grouping statements, whereas ``py`` only allows an expression. - ``#** kwargs`` - ``#** kwargs`` (a.k.a. ``(unpack-mapping kwargs)``) is like ``#* - args``, but collects unmatched keyword arguments into a dictionary. + Python code need not syntactically round-trip if you use ``hy2py`` on a Hy + program that uses ``py`` or ``pys``. For example, comments will be removed. - Each of these special constructs is allowed only once, and has the same - restrictions as in Python; e.g., ``#* args`` must precede ``#** kwargs`` if - both are present. Here's an example with a complex lambda list:: +.. hy:macro:: (pys [string]) - (defn f [a / b [c 3] * d e #** kwargs] - [a b c d e kwargs]) - (print (hy.repr (f 1 2 :d 4 :e 5 :f 6))) - ; => [1 2 3 4 5 {"f" 6}] + As :hy:func:`py `, but the code can consist of zero or more statements, + including compound statements such as ``for`` and ``def``. ``pys`` always + returns ``None``. :: - Type parameters require Python 3.12, and have the semantics specified by - :pep:`695`. The keyword ``:tp`` introduces the list of type parameters. Each - item of the list is a symbol, an annotated symbol (such as ``#^ int T``), or - an unpacked symbol (such as ``#* T`` or ``#** T``). As in Python, unpacking - and annotation can't be used with the same parameter. + (pys "myvar = 5") + (print "myvar is" myvar) -.. hy:macro:: (defmacro [name lambda-list #* body]) + Unlike ``py``, no parentheses are added, because Python doesn't allow + statements to be parenthesized. Instead, the code string is dedented with + :func:`textwrap.dedent` before parsing. Thus you can indent the code to + match the surrounding Hy code when Python would otherwise forbid this, but + beware that significant leading whitespace in embedded string literals will + be removed. - ``defmacro`` is used to define macros. The general format is - ``(defmacro name [parameters] expr)``. +.. hy:macro:: (pragma [#* args]) - The following example defines a macro that can be used to swap order of elements - in code, allowing the user to write code in infix notation, where operator is in - between the operands. + ``pragma`` is used to adjust the state of the compiler. It's called for its + side-effects, and returns ``None``. The arguments are key-value pairs, like a + function call with keyword arguments:: - :strong:`Examples` - :: + (pragma :prag1 value1 :prag2 (get-value2)) - => (defmacro infix [code] - ... (quasiquote ( - ... (unquote (get code 1)) - ... (unquote (get code 0)) - ... (unquote (get code 2))))) + Each key is a literal keyword giving the name of a pragma. Each value is an + arbitrary form, which is evaluated as ordinary Hy code but at compile-time. - :: + The effect of each pragma is locally scoped to its containing function, + class, or comprehension form (other than ``for``), if there is one. - => (infix (1 + 1)) - 2 + Only one pragma is currently implemented: - If ``defmacro`` appears in a function definition, a class definition, or a - comprehension other than :hy:func:`for` (such as :hy:func:`lfor`), the new - macro is defined locally rather than module-wide. +.. _warn-on-core-shadow: - .. note:: ``defmacro`` cannot use keyword arguments, because all values - are passed to macros unevaluated. All arguments are passed - positionally, but they can have default values:: + - ``:warn-on-core-shadow``: If true (the default), :hy:func:`defmacro` and + :hy:func:`require` will raise a warning at compile-time if you define a macro + with the same name as a core macro. Shadowing a core macro in this fashion is + dangerous, because other macros may call your new macro when they meant to + refer to the core macro. - => (defmacro a-macro [a [b 1]] - ... `[~a ~b]) - => (a-macro 2) - [2 1] - => (a-macro 2 3) - [2 3] - => (a-macro :b 3) - [:b 3] +Quoting +~~~~~~~~~~~~ -.. hy:macro:: (if [test true-value false-value]) +.. hy:macro:: (quote [model]) - ``if`` compiles to an :py:keyword:`if` expression (or compound ``if`` statement). The form ``test`` is evaluated and categorized as true or false according to :py:class:`bool`. If the result is true, ``true-value`` is evaluated and returned. Othewise, ``false-value`` is evaluated and returned. - :: + Return the given :ref:`model ` without evaluating it. Or to be more pedantic, ``quote`` complies to code that produces and returns the model it was originally called on. Thus ``quote`` serves as syntactic sugar for model constructors:: - (if (has-money-left account) - (print "Let's go shopping!") - (print "Back to work.")) + (quote a) + ; Equivalent to: (hy.models.Symbol "a") + (quote (+ 1 1)) + ; Equivalent to: (hy.models.Expression [ + ; (hy.models.Symbol "+") + ; (hy.models.Integer 1) + ; (hy.models.Integer 1)]) + + ``quote`` itself is conveniently :ref:`abbreviated ` as the single-quote character ``'``, which needs no parentheses, allowing one to instead write:: + + 'a + '(+ 1 1) See also: - - :hy:func:`do`, to execute several forms as part of any of ``if``'s three arguments. - - :hy:func:`when `, for shorthand for ``(if condition (do …) None)``. - - :hy:func:`cond `, for shorthand for nested ``if`` forms. + - :hy:func:`quasiquote` to substitute values into a quoted form + - :hy:func:`hy.eval` to evaluate models as code + - :hy:func:`hy.repr` to stringify models into Hy source text that uses ``'`` -.. hy:macro:: (await [obj]) +.. hy:macro:: (quasiquote [model]) +.. hy:macro:: (unquote [model]) +.. hy:macro:: (unquote-splice [model]) - ``await`` creates an :ref:`await expression `. It takes exactly one - argument: the object to wait for. :: + ``quasiquote`` is like :hy:func:`quote` except that it treats the model as a template, in which certain special :ref:`expressions ` indicate that some code should be evaluated and its value substituted there. The idea is similar to C's ``sprintf`` or Python's various string-formatting constructs. For example:: - (import asyncio) - (defn :async main [] - (print "hello") - (await (asyncio.sleep 1)) - (print "world")) - (asyncio.run (main)) + (setv x 2) + (quasiquote (+ 1 (unquote x))) ; => '(+ 1 2) -.. hy:macro:: (break) + ``unquote`` indicates code to be evaluated, so ``x`` becomes ``2`` and the ``2`` gets inserted in the parent model. ``quasiquote`` can be :ref:`abbreviated ` as a backtick (\`), with no parentheses, and likewise ``unquote`` can be abbreviated as a tilde (``~``), so one can instead write simply :: - ``break`` compiles to a :py:keyword:`break` statement, which terminates the - enclosing loop. The following example has an infinite ``while`` loop that - ends when the user enters "k":: + `(+ 1 ~x) - (while True - (if (= (input "> ") "k") - (break) - (print "Try again"))) + (In the bulk of Lisp tradition, unquotation is written ``,``. Hy goes with Clojure's choice of ``~``, which has the advantage of being more visible in most programming fonts.) - In a loop with multiple iteration clauses, such as ``(for [x xs y ys] …)``, - ``break`` only breaks out of the innermost iteration, not the whole form. To - jump out of the whole form, enclose it in a :hy:func:`block - ` and use ``block-ret`` instead of ``break``. In - the case of :hy:func:`for`, but not :hy:func:`lfor` and the other - comprehension forms, you may also enclose it in a function and use - :hy:func:`return`. + Quasiquotation is convenient for writing macros:: -.. hy:macro:: (chainc [#* args]) + (defmacro set-foo [value] + `(setv foo ~value)) + (set-foo (+ 1 2 3)) + (print foo) ; => 6 - ``chainc`` creates a :ref:`comparison expression `. It isn't - required for unchained comparisons, which have only one comparison operator, - nor for chains of the same operator. For those cases, you can use the - comparison operators directly with Hy's usual prefix syntax, as in ``(= x 1)`` - or ``(< 1 2 3)``. The use of ``chainc`` is to construct chains of - heterogeneous operators, such as ``x <= y < z``. It uses an infix syntax with - the general form + Another kind of unquotation operator, ``unquote-splice``, abbreviated ``~@``, is analogous to ``unpack-iterable`` in that it splices an iterable object into the sequence of the parent :ref:`sequential model `. Compare the effects of ``unquote`` to ``unquote-splice``:: - :: + (setv X [1 2 3]) + (hy.repr `[a b ~X c d ~@X e f]) + ; => '[a b [1 2 3] c d 1 2 3 e f] - (chainc ARG OP ARG OP ARG…) + If ``unquote-splice`` is given any sort of false value (such as ``None``), it's treated as an empty list. To be precise, ``~@x`` splices in the result of ``(or x [])``. - Hence, ``(chainc x <= y < z)`` is equivalent to ``(and (<= x y) (< y z))``, - including short-circuiting, except that ``y`` is only evaluated once. + Note that while a symbol name can begin with ``@`` in Hy, ``~@`` takes precedence in the parser, so if you want to unquote the symbol ``@foo`` with ``~``, you must use whitespace to separate ``~`` and ``@``, as in ``~ @foo``. - Each ``ARG`` is an arbitrary form, which does not itself use infix syntax. Use - :hy:func:`py ` if you want fully Python-style operator syntax. You can - also nest ``chainc`` forms, although this is rarely useful. Each ``OP`` is a - literal comparison operator; other forms that resolve to a comparison operator - are not allowed. +Assignment, mutation, and annotation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - At least two ``ARG``\ s and one ``OP`` are required, and every ``OP`` must be - followed by an ``ARG``. +.. hy:macro:: (setv [#* args]) - As elsewhere in Hy, the equality operator is spelled ``=``, not ``==`` as in - Python. + ``setv`` compiles to an :ref:`assignment statement ` (see :hy:func:`setx` for assignment expressions), which sets the value of a variable or some other assignable expression. It requires an even number of arguments, and always returns ``None``. The most common case is two arguments, where the first is a symbol:: + (setv websites 103) + (print websites) ; => 103 -.. hy:macro:: (continue) + Additional pairs of arguments are equivalent to several two-argument ``setv`` calls, in the given order. Thus, the semantics are like Common Lisp's ``setf`` rather than ``psetf``. :: - ``continue`` compiles to a :py:keyword:`continue` statement, which returns - execution to the start of a loop. In the following example, ``(.append - output x)`` is executed on each iteration, whereas ``(.append evens x)`` is - only executed for even numbers. + (setv x 1 y x x 2) + (print x y) ; => 2 1 - :: + All the same kinds of complex assignment targets are allowed as in Python. So, you can use list assignment to assign in parallel. (As in Python, tuple and list syntax are equivalent for this purpose; Hy differs from Python merely in that its list syntax is shorter than its tuple syntax.) :: - (setv output [] evens []) - (for [x (range 10)] - (.append output x) - (when (% x 2) - (continue)) - (.append evens x)) + (setv [x y] [y x]) ; Swaps the values of `x` and `y` - In a loop with multiple iteration clauses, such as ``(for [x xs y ys] …)``, - ``continue`` applies to the innermost iteration, not the whole form. To jump - to the next step of an outer iteration, try rewriting your loop as multiple - nested loops and interposing a :hy:func:`block `, as in - ``(for [x xs] (block (for [y ys] …)))``. You can then use ``block-ret`` in - place of ``continue``. + Unpacking assignment looks like this (see :hy:func:`unpack-iterable`):: -.. hy:macro:: (do [#* body]) + (setv [letter1 letter2 #* others] "abcdefg") + (print letter1 letter2 (hy.repr others)) + ; => a b ["c" "d" "e" "f" "g"] - ``do`` (called ``progn`` in some Lisps) takes any number of forms, - evaluates them, and returns the value of the last one, or ``None`` if no - forms were provided. :: + See :hy:func:`let` to simulate more traditionally Lispy block-level scoping. - (+ 1 (do (setv x (+ 1 1)) x)) ; => 3 +.. hy:macro:: (setx [target value]) -.. hy:macro:: (do-mac [#* body]) + ``setx`` compiles to an assignment expression. Thus, unlike :hy:func:`setv`, it returns the assigned value. It takes exactly two arguments, and the target must be a bare symbol. Python 3.8 or later is required. :: - ``do-mac`` evaluates its arguments (in order) at compile time, and leaves behind the value of the last argument (``None`` if no arguments were provided) as code to be run. The effect is similar to defining and then immediately calling a nullary macro, hence the name, which stands for "do macro". :: + (when (> (setx x (+ 1 2)) 0) + (print x "is greater than 0")) + ; => 3 is greater than 0 - (do-mac `(setv ~(hy.models.Symbol (* "x" 5)) "foo")) - ; Expands to: (setv xxxxx "foo") - (print xxxxx) - ; => "foo" +.. hy:macro:: (let [bindings #* body]) - Contrast with :hy:func:`eval-and-compile`, which evaluates the same code at compile-time and run-time, instead of using the result of the compile-time run as code for run-time. ``do-mac`` is also similar to Common Lisp's SHARPSIGN DOT syntax (``#.``), from which it differs by evaluating at compile-time rather than read-time. + ``let`` creates lexically-scoped names for local variables. This form takes a + list of binding pairs followed by a *body* which gets executed. A let-bound + name ceases to refer to that local outside the ``let`` form, but arguments in + nested functions and bindings in nested ``let`` forms can shadow these names. -.. hy:macro:: (for [#* args]) - ``for`` compiles to one or more :py:keyword:`for` statements, which execute - code repeatedly for each element of an iterable object. The return values of - the forms are discarded and the ``for`` form returns ``None``. :: + :strong:`Examples` - (for [x [1 2 3]] - (print "iterating") - (print x)) - ; Output: iterating 1 iterating 2 iterating 3 + :: - The first argument of ``for``, in square brackets, specifies how to loop. A - simple and common case is ``[variable values]``, where ``values`` is a form - that evaluates to an iterable object (such as a list) and ``variable`` is a - symbol specifiying the name for each element. Subsequent arguments to ``for`` - are body forms to be evaluated for each iteration of the loop. + => (let [x 5 ; creates new local bound names 'x and 'y + y 6] + ... (print x y) + ... (let [x 7] ; new local and name binding that shadows 'x + ... (print x y)) + ... (print x y)) ; 'x refers to the first local again + 5 6 + 7 6 + 5 6 - More generally, the first argument of ``for`` allows the same types of - clauses as :hy:func:`lfor`:: + ``let`` can also bind names using + Python's `extended iterable unpacking`_ syntax to destructure iterables:: - (for [x [1 2 3] :if (!= x 2) y [7 8]] - (print x y)) - ; Output: 1 7 1 8 3 7 3 8 + => (let [[head #* tail] #(0 1 2)] + ... [head tail]) + [0 [1 2]] - In particular, you can use an ``:async`` clause to get the equivalent of - Python's :py:keyword:`async for`:: + Basic assignments (e.g. ``setv``, ``+=``) will update the local + variable named by a let binding when they assign to a let-bound name. + But assignments via ``import`` are always hoisted to normal Python + scope, and likewise, ``defn`` or ``defclass`` will assign the + function or class in the Python scope, even if it shares the name of + a let binding. To avoid this hoisting, use + ``importlib.import_module``, ``fn``, or ``type`` (or whatever + metaclass) instead. - (import asyncio) - (defn :async numbers [] - (yield 1) - (yield 2)) - (asyncio.run ((fn :async [] - (for [:async x (numbers)] - (print x))))) + If ``lfor``, ``sfor``, ``dfor``, or ``gfor`` (but not ``for``) is in + the body of a ``let``, assignments in iteration clauses and ``:setv`` + clauses will create a new variable in the comprehenion form's own + scope, without touching any outer let-bound variable of the same + name. - The last argument of ``for`` can be an ``(else …)`` form. This form is - executed after the last iteration of the ``for``\'s outermost iteration - clause, but only if that outermost loop terminates normally. If it's jumped - out of with e.g. ``break``, the ``else`` is ignored. :: + Like the ``let*`` of many other Lisps, ``let`` executes the variable + assignments one-by-one, in the order written:: - (for [x [1 2 3]] - (print x) - (when (= x 2) - (break)) - (else (print "loop finished"))) + => (let [x 5 + ... y (+ x 1)] + ... (print x y)) + 5 6 -.. hy:macro:: (assert [condition [label None]]) + => (let [x 1 + ... x (fn [] x)] + ... (x)) + 1 - ``assert`` compiles to an :py:keyword:`assert` statement, which checks - whether a condition is true. The first argument, specifying the condition to - check, is mandatory, whereas the second, which will be passed to - :py:class:`AssertionError`, is optional. The whole form is only evaluated - when :py:data:`__debug__` is true, and the second argument is only evaluated - when :py:data:`__debug__` is true and the condition fails. ``assert`` always - returns ``None``. :: + Note that let-bound variables continue to exist in the surrounding + Python scope. As such, ``let``-bound objects may not be eligible for + garbage collection as soon as the ``let`` ends. To ensure there are + no references to ``let``-bound objects as soon as possible, use + ``del`` at the end of the ``let``, or wrap the ``let`` in a function. - (assert (= 1 2) "one should equal two") - ; AssertionError: one should equal two + .. _extended iterable unpacking: https://www.python.org/dev/peps/pep-3132/#specification .. hy:macro:: (global [#* syms]) @@ -403,649 +352,508 @@ base names, such that ``hy.core.macros.foo`` can be called as just ``foo``. (f) (print a b) ; => 2 10 +.. hy:macro:: (nonlocal [#* syms]) -.. hy:macro:: (import [#* forms]) - - ``import`` compiles to an :py:keyword:`import` statement, which makes objects - in a different module available in the current module. It always returns - ``None``. Hy's syntax for the various kinds of import looks like this:: - - ;; Import each of these modules - ;; Python: import sys, os.path - (import sys os.path) + Similar to :hy:func:`global`, but names can be declared in any enclosing + scope. ``nonlocal`` compiles to a :py:keyword:`global` statement for any + names originally defined in the global scope, and a :py:keyword:`nonlocal` + statement for all other names. :: - ;; Import several names from a single module - ;; Python: from os.path import exists, isdir as is_dir, isfile - (import os.path [exists isdir :as dir? isfile]) + (setv a 1 b 1) + (defn f [] + (setv c 10 d 10) + (defn g [] + (nonlocal a c) + (setv a 2 b 2 + c 20 d 20)) + (print a b c d) ; => 1 1 10 10 + (g) + (print a b c d)) ; => 2 1 20 10 + (f) - ;; Import with an alias - ;; Python: import sys as systest - (import sys :as systest) +.. hy:macro:: (del [#* args]) - ;; You can list as many imports as you like of different types. - ;; Python: - ;; from tests.resources import kwtest, function_with_a_dash - ;; from os.path import exists, isdir as is_dir, isfile as is_file - ;; import sys as systest - (import tests.resources [kwtest function-with-a-dash] - os.path [exists - isdir :as dir? - isfile :as file?] - sys :as systest) + ``del`` compiles to a :py:keyword:`del` statement, which deletes variables + or other assignable expressions. It always returns ``None``. :: - ;; Import all module functions into current namespace - ;; Python: from sys import * - (import sys *) + (del foo (get mydict "mykey") myobj.myattr) - ``__all__`` can be set to control what's imported by ``import *``, as in - Python, but beware that all names in ``__all__`` must be :ref:`mangled - `. The macro :hy:func:`export ` is a handy - way to set ``__all__`` in a Hy program. +.. hy:macro:: (annotate [value type]) -.. hy:macro:: (eval-and-compile [#* body]) + ``annotate`` and its shorthand form ``#^`` are used to denote annotations, + including type hints, in three different contexts: - ``eval-and-compile`` takes any number of forms as arguments. The input forms are evaluated as soon as the ``eval-and-compile`` form is compiled, then left in the program so they can be executed at run-time as usual; contrast with :hy:func:`eval-when-compile`. So, if you compile and immediately execute a program (as calling ``hy foo.hy`` does when ``foo.hy`` doesn't have an up-to-date byte-compiled version), ``eval-and-compile`` forms will be evaluated twice. For example, the following program :: + - Standalone variable annotations (:pep:`526`) + - Variable annotations in a :hy:func:`setv` call + - Function-parameter annotations (:pep:`3107`) - (eval-when-compile - (print "Compiling")) - (print "Running") - (eval-and-compile - (print "Hi")) + The difference between ``annotate`` and ``#^`` is that ``annotate`` requires + parentheses and takes the name to be annotated first (like Python), whereas + ``#^`` doesn't require parentheses (it only applies to the next two forms) + and takes the type second:: - prints + (setv (annotate x int) 1) + (setv #^ int x 1) - .. code-block:: text + The order difference is not merely visual: ``#^`` actually evaluates the + type first. - Compiling - Hi - Running - Hi + Here are examples with ``#^`` for all the places you can use annotations:: - The return value of ``eval-and-compile`` is its final argument, as for :hy:func:`do`. + ; Annotate the variable `x` as an `int` (equivalent to `x: int`). + #^ int x + ; You can annotate with expressions (equivalent to `y: f(x)`). + #^(f x) y - One possible use of ``eval-and-compile`` is to make a function available both at compile-time (so a macro can call it while expanding) and run-time (so it can be called like any other function):: + ; Annotations with an assignment: each annotation `(int, str)` + ; covers the term that immediately follows. + ; Equivalent to `x: int = 1; y = 2; z: str = 3` + (setv #^ int x 1 y 2 #^ str z 3) - (eval-and-compile - (defn add [x y] - (+ x y))) + ; Annotate `a` as an `int`, `c` as an `int`, and `b` as a `str`. + ; Equivalent to `def func(a: int, b: str = None, c: int = 1): ...` + (defn func [#^ int a #^ str [b None] #^ int [c 1]] ...) - (defmacro m [x] - (add x 2)) + ; Function return annotations come before the function name (if + ; it exists). + (defn #^ int add1 [#^ int x] (+ x 1)) + (fn #^ int [#^ int y] (+ y 2)) - (print (m 3)) ; prints 5 - (print (add 3 6)) ; prints 9 + For annotating items with generic types, the :hy:func:`of ` + macro will likely be of use. - Had the ``defn`` not been wrapped in ``eval-and-compile``, ``m`` wouldn't be able to call ``add``, because when the compiler was expanding ``(m 3)``, ``add`` wouldn't exist yet. + An issue with type annotations is that, as of this writing, we know of no Python type-checker that can work with :py:mod:`ast` objects or bytecode files. They all need Python source text. So you'll have to translate your Hy with ``hy2py`` in order to actually check the types. - While ``eval-and-compile`` executes the same code at both compile-time and run-time, bear in mind that the same code can have different meanings in the two contexts. Consider, for example, issues of scoping:: +.. hy:macro:: (deftype [args]) - (eval-when-compile - (print "Compiling")) - (print "Running") - (eval-and-compile - (setv x 1)) - (defn f [] - (setv x 2) - (eval-and-compile - (setv x 3)) - (print "local x =" x)) - (f) - (eval-and-compile - (print "global x =" x)) + ``deftype`` compiles to a :py:keyword:`type` statement, which defines a + type alias. It requires Python 3.12. Its arguments optionally begin with + ``:tp`` and a list of type parameters (as in :hy:func:`defn`), then specify + the name for the new alias and its value. :: - The form ``(setv x 3)`` above refers to the global ``x`` at compile-time, but the local ``x`` at run-time, so the result is: + (deftype IntOrStr (| int str)) + (deftype :tp [T] ListOrSet (| (get list T) (get set T))) - .. code-block:: text +Subsetting +~~~~~~~~~~~~ - Compiling - global x = 3 - Running - local x = 3 - global x = 1 +.. _dot: -.. hy:macro:: (eval-when-compile [#* body]) +.. hy:data:: . - ``eval-when-compile`` executes the given forms at compile-time, but discards them at run-time and simply returns :data:`None` instead; contrast :hy:func:`eval-and-compile`. Hence, while ``eval-when-compile`` doesn't directly contribute code to the final program, it can change Hy's state while compiling, as by defining a function:: + The dot macro ``.`` compiles to one or more :ref:`attribute references + `, which select an attribute of an object. The + first argument, which is required, can be an arbitrary form. With no further + arguments, ``.`` is a no-op. Additional symbol arguments are understood as a + chain of attributes, so ``(. foo bar)`` compiles to ``foo.bar``, and ``(. a b + c d)`` compiles to ``a.b.c.d``. - (eval-when-compile - (defn add [x y] - (+ x y))) + As a convenience, ``.`` supports two other kinds of arguments in place of a + plain attribute. A parenthesized expression is understood as a method call: + ``(. foo (bar a b))`` compiles to ``foo.bar(a, b)``. A bracketed form is + understood as a subscript: ``(. foo ["bar"])`` compiles to ``foo["bar"]``. + All these options can be mixed and matched in a single ``.`` call, so :: - (defmacro m [x] - (add x 2)) + (. a (b 1 2) c [d] [(e 3 4)]) - (print (m 3)) ; prints 5 - (print (add 3 6)) ; raises NameError: name 'add' is not defined + compiles to -.. hy:macro:: (lfor [#* args]) + .. code-block:: python - The comprehension forms ``lfor``, :hy:func:`sfor`, :hy:func:`dfor`, :hy:func:`gfor`, and :hy:func:`for` - are used to produce various kinds of loops, including Python-style - :ref:`comprehensions `. ``lfor`` in particular - can create a list comprehension. A simple use of ``lfor`` is:: + a.b(1, 2).c[d][e(3, 4)] - (lfor x (range 5) (* 2 x)) ; => [0 2 4 6 8] + :ref:`Dotted identifiers ` provide syntactic sugar for + common uses of this macro. In particular, syntax like ``foo.bar`` ends up + meaning the same thing in Hy as in Python. Also, :hy:func:`get + ` is another way to subscript in Hy. - ``x`` is the name of a new variable, which is bound to each element of - ``(range 5)``. Each such element in turn is used to evaluate the value - form ``(* 2 x)``, and the results are accumulated into a list. +.. hy:macro:: (unpack-iterable [form]) +.. hy:macro:: (unpack-mapping [form]) - Here's a more complex example:: + (Also known as the splat operator, star operator, argument expansion, argument + explosion, argument gathering, and varargs, among others...) - (lfor - x (range 3) - y (range 3) - :if (!= x y) - :setv total (+ x y) - [x y total]) - ; => [[0 1 1] [0 2 2] [1 0 1] [1 2 3] [2 0 2] [2 1 3]] + ``unpack-iterable`` and ``unpack-mapping`` allow an iterable or mapping + object (respectively) to provide positional or keywords arguments + (respectively) to a function. - When there are several iteration clauses (here, the pairs of forms ``x - (range 3)`` and ``y (range 3)``), the result works like a nested loop or - Cartesian product: all combinations are considered in lexicographic - order. + :: - The general form of ``lfor`` is:: + => (defn f [a b c d] [a b c d]) + => (f (unpack-iterable [1 2]) (unpack-mapping {"c" 3 "d" 4})) + [1 2 3 4] - (lfor CLAUSES VALUE) + ``unpack-iterable`` is usually written with the shorthand ``#*``, and + ``unpack-mapping`` with ``#**``. - where the ``VALUE`` is an arbitrary form that is evaluated to produce - each element of the result list, and ``CLAUSES`` is any number of - clauses. There are several types of clauses: + :: - - Iteration clauses, which look like ``LVALUE ITERABLE``. The ``LVALUE`` - is usually just a symbol, but could be something more complicated, - like ``[x y]``. - - ``:async LVALUE ITERABLE``, which is an asynchronous form of - iteration clause per Python's :py:keyword:`async for`. - - ``:do FORM``, which simply evaluates the ``FORM``. If you use - ``(continue)`` or ``(break)`` here, it will apply to the innermost - iteration clause before the ``:do``. - - ``:setv LVALUE RVALUE``, which is equivalent to ``:do (setv LVALUE - RVALUE)``. - - ``:if CONDITION``, which is equivalent to ``:do (when (not CONDITION) - (continue))``. + => (f #* [1 2] #** {"c" 3 "d" 4}) + [1 2 3 4] - For ``lfor``, ``sfor``, ``gfor``, and ``dfor``, variables defined by - an iteration clause or ``:setv`` are not visible outside the form. - However, variables defined within the body, as with a ``setx`` - expression, will be visible outside the form. - In ``for``, by contrast, iteration and ``:setv`` clauses share the - caller's scope and are visible outside the form. + Unpacking is allowed in a variety of contexts, and you can unpack + more than once in one expression (:pep:`3132`, :pep:`448`). -.. hy:macro:: (dfor [#* args]) + :: - ``dfor`` creates a :ref:`dictionary comprehension `. Its syntax - is the same as that of :hy:func:`lfor` except that it takes two trailing - arguments. The first is a form producing the key of each dictionary - element, and the second produces the value. Thus:: + => (setv [a #* b c] [1 2 3 4 5]) + => [a b c] + [1 [2 3 4] 5] + => [#* [1 2] #* [3 4]] + [1 2 3 4] + => {#** {1 2} #** {3 4}} + {1 2 3 4} + => (f #* [1] #* [2] #** {"c" 3} #** {"d" 4}) + [1 2 3 4] - => (dfor x (range 5) x (* x 10)) - {0 0 1 10 2 20 3 30 4 40} +Conditionals and basic loops +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. hy:macro:: (if [test true-value false-value]) -.. hy:macro:: (gfor [#* args]) + ``if`` compiles to an :py:keyword:`if` expression (or compound ``if`` statement). The form ``test`` is evaluated and categorized as true or false according to :py:class:`bool`. If the result is true, ``true-value`` is evaluated and returned. Othewise, ``false-value`` is evaluated and returned. + :: - ``gfor`` creates a :ref:`generator expression `. Its syntax - is the same as that of :hy:func:`lfor`. The difference is that ``gfor`` returns - an iterator, which evaluates and yields values one at a time:: + (if (has-money-left account) + (print "Let's go shopping!") + (print "Back to work.")) - => (import itertools [count take-while]) - => (setv accum []) - => (list (take-while - ... (fn [x] (< x 5)) - ... (gfor x (count) :do (.append accum x) x))) - [0 1 2 3 4] - => accum - [0 1 2 3 4 5] + See also: -.. hy:macro:: (sfor [#* args]) + - :hy:func:`do`, to execute several forms as part of any of ``if``'s three arguments. + - :hy:func:`when `, for shorthand for ``(if condition (do …) None)``. + - :hy:func:`cond `, for shorthand for nested ``if`` forms. - ``sfor`` creates a :ref:`set comprehension `. ``(sfor CLAUSES VALUE)`` is - equivalent to ``(set (lfor CLAUSES VALUE))``. See :hy:func:`lfor`. +.. hy:automacro:: hy.core.macros.when -.. hy:macro:: (setv [#* args]) - - ``setv`` compiles to an :ref:`assignment statement ` (see :hy:func:`setx` for assignment expressions), which sets the value of a variable or some other assignable expression. It requires an even number of arguments, and always returns ``None``. The most common case is two arguments, where the first is a symbol:: - - (setv websites 103) - (print websites) ; => 103 - - Additional pairs of arguments are equivalent to several two-argument ``setv`` calls, in the given order. Thus, the semantics are like Common Lisp's ``setf`` rather than ``psetf``. :: - - (setv x 1 y x x 2) - (print x y) ; => 2 1 - - All the same kinds of complex assignment targets are allowed as in Python. So, you can use list assignment to assign in parallel. (As in Python, tuple and list syntax are equivalent for this purpose; Hy differs from Python merely in that its list syntax is shorter than its tuple syntax.) :: - - (setv [x y] [y x]) ; Swaps the values of `x` and `y` - - Unpacking assignment looks like this (see :hy:func:`unpack-iterable`):: - - (setv [letter1 letter2 #* others] "abcdefg") - (print letter1 letter2 (hy.repr others)) - ; => a b ["c" "d" "e" "f" "g"] - - See :hy:func:`let` to simulate more traditionally Lispy block-level scoping. - -.. hy:macro:: (setx [target value]) - - ``setx`` compiles to an assignment expression. Thus, unlike :hy:func:`setv`, it returns the assigned value. It takes exactly two arguments, and the target must be a bare symbol. Python 3.8 or later is required. :: - - (when (> (setx x (+ 1 2)) 0) - (print x "is greater than 0")) - ; => 3 is greater than 0 - -.. hy:macro:: (let [bindings #* body]) - - ``let`` creates lexically-scoped names for local variables. This form takes a - list of binding pairs followed by a *body* which gets executed. A let-bound - name ceases to refer to that local outside the ``let`` form, but arguments in - nested functions and bindings in nested ``let`` forms can shadow these names. - - - :strong:`Examples` - - :: - - => (let [x 5 ; creates new local bound names 'x and 'y - y 6] - ... (print x y) - ... (let [x 7] ; new local and name binding that shadows 'x - ... (print x y)) - ... (print x y)) ; 'x refers to the first local again - 5 6 - 7 6 - 5 6 - - ``let`` can also bind names using - Python's `extended iterable unpacking`_ syntax to destructure iterables:: - - => (let [[head #* tail] #(0 1 2)] - ... [head tail]) - [0 [1 2]] - - Basic assignments (e.g. ``setv``, ``+=``) will update the local - variable named by a let binding when they assign to a let-bound name. - But assignments via ``import`` are always hoisted to normal Python - scope, and likewise, ``defn`` or ``defclass`` will assign the - function or class in the Python scope, even if it shares the name of - a let binding. To avoid this hoisting, use - ``importlib.import_module``, ``fn``, or ``type`` (or whatever - metaclass) instead. - - If ``lfor``, ``sfor``, ``dfor``, or ``gfor`` (but not ``for``) is in - the body of a ``let``, assignments in iteration clauses and ``:setv`` - clauses will create a new variable in the comprehenion form's own - scope, without touching any outer let-bound variable of the same - name. - - Like the ``let*`` of many other Lisps, ``let`` executes the variable - assignments one-by-one, in the order written:: - - => (let [x 5 - ... y (+ x 1)] - ... (print x y)) - 5 6 - - => (let [x 1 - ... x (fn [] x)] - ... (x)) - 1 - - Note that let-bound variables continue to exist in the surrounding - Python scope. As such, ``let``-bound objects may not be eligible for - garbage collection as soon as the ``let`` ends. To ensure there are - no references to ``let``-bound objects as soon as possible, use - ``del`` at the end of the ``let``, or wrap the ``let`` in a function. - - .. _extended iterable unpacking: https://www.python.org/dev/peps/pep-3132/#specification - - -.. hy:macro:: (match [subject #* cases]) - - The ``match`` form creates a :ref:`match statement `. It - requires Python 3.10 or later. The first argument should be the subject, - and any remaining arguments should be pairs of patterns and results. The - ``match`` form returns the value of the corresponding result, or - ``None`` if no case matched. :: - - (match (+ 1 1) - 1 "one" - 2 "two" - 3 "three") - ; => "two" - - You can use :hy:func:`do` to build a complex result form. Patterns, as - in Python match statements, are interpreted specially and can't be - arbitrary forms. Use ``(| …)`` for OR patterns, ``PATTERN :as NAME`` for - AS patterns, and syntax like the usual Hy syntax for literal, capture, - value, sequence, mapping, and class patterns. Guards are specified - with ``:if FORM``. Here's a more complex example:: - - (match #(100 200) - [100 300] "Case 1" - [100 200] :if flag "Case 2" - [900 y] f"Case 3, y: {y}" - [100 (| 100 200) :as y] f"Case 4, y: {y}" - _ "Case 5, I match anything!") - - This will match case 2 if ``flag`` is true and case 4 otherwise. - - ``match`` can also match against class instances by keyword (or - positionally if its ``__match_args__`` attribute is defined; see :pep:`636`):: - - (import dataclasses [dataclass]) - (defclass [dataclass] Point [] - #^ int x - #^ int y) - (match (Point 1 2) - (Point 1 x) :if (= (% x 2) 0) x) ; => 2 - - It's worth emphasizing that ``match`` is a pattern-matching construct - rather than a generic `switch - `_ construct, and - retains all of Python's limitations on match patterns. For example, you - can't match against the value of a variable. For more flexible branching - constructs, see Hyrule's :hy:func:`branch ` and - :hy:func:`case `, or simply use :hy:func:`cond - `. +.. hy:automacro:: hy.core.macros.cond -.. hy:macro:: (defclass [arg1 #* args]) - - ``defclass`` compiles to a :py:keyword:`class` statement, which creates a - new class. It always returns ``None``. Only one argument, specifying the - name of the new class as a symbol, is required. A list of :term:`decorators - ` (and type parameters, in the same way as for - :hy:func:`defn`) may be provided before the class name. After the name comes - a list of superclasses (use the empty list ``[]`` for the typical case of no - superclasses) and any number of body forms, the first of which may be a - :term:`py:docstring`. :: - - (defclass [decorator1 decorator2] :tp [T1 T2] MyClass [SuperClass1 SuperClass2] - "A class that does things at times." - - (setv - attribute1 value1 - attribute2 value2) - - (defn method1 [self arg1 arg2] - …) - - (defn method2 [self arg1 arg2] - …)) +.. hy:macro:: (while [condition #* body]) -.. hy:macro:: (del [#* args]) + ``while`` compiles to a :py:keyword:`while` statement, which executes some + code as long as a condition is met. The first argument to ``while`` is the + condition, and any remaining forms constitute the body. It always returns + ``None``. :: - ``del`` compiles to a :py:keyword:`del` statement, which deletes variables - or other assignable expressions. It always returns ``None``. :: + (while True + (print "Hello world!")) - (del foo (get mydict "mykey") myobj.myattr) + The last form of a ``while`` loop can be an ``else`` clause, which is + executed after the loop terminates, unless it exited abnormally (e.g., with + ``break``). So, :: -.. hy:macro:: (nonlocal [#* syms]) + (setv x 2) + (while x + (print "In body") + (-= x 1) + (else + (print "In else"))) - Similar to :hy:func:`global`, but names can be declared in any enclosing - scope. ``nonlocal`` compiles to a :py:keyword:`global` statement for any - names originally defined in the global scope, and a :py:keyword:`nonlocal` - statement for all other names. :: + prints :: - (setv a 1 b 1) - (defn f [] - (setv c 10 d 10) - (defn g [] - (nonlocal a c) - (setv a 2 b 2 - c 20 d 20)) - (print a b c d) ; => 1 1 10 10 - (g) - (print a b c d)) ; => 2 1 20 10 - (f) + In body + In body + In else -.. hy:macro:: (py [string]) + If you put a ``break`` or ``continue`` form in the condition of a ``while`` + loop, it will apply to the very same loop rather than an outer loop, even if + execution is yet to ever reach the loop body. (Hy compiles a ``while`` loop + with statements in its condition by rewriting it so that the condition is + actually in the body.) So, :: - ``py`` parses the given Python code at compile-time and inserts the result into - the generated abstract syntax tree. Thus, you can mix Python code into a Hy - program. Only a Python expression is allowed, not statements; use - :hy:func:`pys ` if you want to use Python statements. The value of the - expression is returned from the ``py`` form. :: + (for [x [1]] + (print "In outer loop") + (while + (do + (print "In condition") + (break) + (print "This won't print.") + True) + (print "This won't print, either.")) + (print "At end of outer loop")) - (print "A result from Python:" (py "'hello' + 'world'")) + prints :: - The code must be given as a single string literal, but you can still use - macros, :hy:func:`hy.eval `, and related tools to construct the ``py`` form. If - having to backslash-escape internal double quotes is getting you down, try a - :ref:`bracket string `. If you want to evaluate some Python - code that's only defined at run-time, try the standard Python function - :func:`eval`. + In outer loop + In condition + At end of outer loop - The code is implicitly wrapped in parentheses so Python won't give you grief - about indentation. After all, Python's indentation rules are only useful for - grouping statements, whereas ``py`` only allows an expression. +.. hy:macro:: (break) - Python code need not syntactically round-trip if you use ``hy2py`` on a Hy - program that uses ``py`` or ``pys``. For example, comments will be removed. + ``break`` compiles to a :py:keyword:`break` statement, which terminates the + enclosing loop. The following example has an infinite ``while`` loop that + ends when the user enters "k":: -.. hy:macro:: (pys [string]) + (while True + (if (= (input "> ") "k") + (break) + (print "Try again"))) - As :hy:func:`py `, but the code can consist of zero or more statements, - including compound statements such as ``for`` and ``def``. ``pys`` always - returns ``None``. :: + In a loop with multiple iteration clauses, such as ``(for [x xs y ys] …)``, + ``break`` only breaks out of the innermost iteration, not the whole form. To + jump out of the whole form, enclose it in a :hy:func:`block + ` and use ``block-ret`` instead of ``break``. In + the case of :hy:func:`for`, but not :hy:func:`lfor` and the other + comprehension forms, you may also enclose it in a function and use + :hy:func:`return`. - (pys "myvar = 5") - (print "myvar is" myvar) +.. hy:macro:: (continue) - Unlike ``py``, no parentheses are added, because Python doesn't allow - statements to be parenthesized. Instead, the code string is dedented with - :func:`textwrap.dedent` before parsing. Thus you can indent the code to - match the surrounding Hy code when Python would otherwise forbid this, but - beware that significant leading whitespace in embedded string literals will - be removed. + ``continue`` compiles to a :py:keyword:`continue` statement, which returns + execution to the start of a loop. In the following example, ``(.append + output x)`` is executed on each iteration, whereas ``(.append evens x)`` is + only executed for even numbers. -.. hy:macro:: (quasiquote [model]) -.. hy:macro:: (unquote [model]) -.. hy:macro:: (unquote-splice [model]) + :: - ``quasiquote`` is like :hy:func:`quote` except that it treats the model as a template, in which certain special :ref:`expressions ` indicate that some code should be evaluated and its value substituted there. The idea is similar to C's ``sprintf`` or Python's various string-formatting constructs. For example:: + (setv output [] evens []) + (for [x (range 10)] + (.append output x) + (when (% x 2) + (continue)) + (.append evens x)) - (setv x 2) - (quasiquote (+ 1 (unquote x))) ; => '(+ 1 2) + In a loop with multiple iteration clauses, such as ``(for [x xs y ys] …)``, + ``continue`` applies to the innermost iteration, not the whole form. To jump + to the next step of an outer iteration, try rewriting your loop as multiple + nested loops and interposing a :hy:func:`block `, as in + ``(for [x xs] (block (for [y ys] …)))``. You can then use ``block-ret`` in + place of ``continue``. - ``unquote`` indicates code to be evaluated, so ``x`` becomes ``2`` and the ``2`` gets inserted in the parent model. ``quasiquote`` can be :ref:`abbreviated ` as a backtick (\`), with no parentheses, and likewise ``unquote`` can be abbreviated as a tilde (``~``), so one can instead write simply :: +Comprehensions +~~~~~~~~~~~~~~ - `(+ 1 ~x) +.. hy:macro:: (for [#* args]) - (In the bulk of Lisp tradition, unquotation is written ``,``. Hy goes with Clojure's choice of ``~``, which has the advantage of being more visible in most programming fonts.) + ``for`` compiles to one or more :py:keyword:`for` statements, which execute + code repeatedly for each element of an iterable object. The return values of + the forms are discarded and the ``for`` form returns ``None``. :: - Quasiquotation is convenient for writing macros:: + (for [x [1 2 3]] + (print "iterating") + (print x)) + ; Output: iterating 1 iterating 2 iterating 3 - (defmacro set-foo [value] - `(setv foo ~value)) - (set-foo (+ 1 2 3)) - (print foo) ; => 6 + The first argument of ``for``, in square brackets, specifies how to loop. A + simple and common case is ``[variable values]``, where ``values`` is a form + that evaluates to an iterable object (such as a list) and ``variable`` is a + symbol specifiying the name for each element. Subsequent arguments to ``for`` + are body forms to be evaluated for each iteration of the loop. - Another kind of unquotation operator, ``unquote-splice``, abbreviated ``~@``, is analogous to ``unpack-iterable`` in that it splices an iterable object into the sequence of the parent :ref:`sequential model `. Compare the effects of ``unquote`` to ``unquote-splice``:: + More generally, the first argument of ``for`` allows the same types of + clauses as :hy:func:`lfor`:: - (setv X [1 2 3]) - (hy.repr `[a b ~X c d ~@X e f]) - ; => '[a b [1 2 3] c d 1 2 3 e f] + (for [x [1 2 3] :if (!= x 2) y [7 8]] + (print x y)) + ; Output: 1 7 1 8 3 7 3 8 - If ``unquote-splice`` is given any sort of false value (such as ``None``), it's treated as an empty list. To be precise, ``~@x`` splices in the result of ``(or x [])``. + In particular, you can use an ``:async`` clause to get the equivalent of + Python's :py:keyword:`async for`:: - Note that while a symbol name can begin with ``@`` in Hy, ``~@`` takes precedence in the parser, so if you want to unquote the symbol ``@foo`` with ``~``, you must use whitespace to separate ``~`` and ``@``, as in ``~ @foo``. + (import asyncio) + (defn :async numbers [] + (yield 1) + (yield 2)) + (asyncio.run ((fn :async [] + (for [:async x (numbers)] + (print x))))) -.. hy:macro:: (quote [model]) + The last argument of ``for`` can be an ``(else …)`` form. This form is + executed after the last iteration of the ``for``\'s outermost iteration + clause, but only if that outermost loop terminates normally. If it's jumped + out of with e.g. ``break``, the ``else`` is ignored. :: - Return the given :ref:`model ` without evaluating it. Or to be more pedantic, ``quote`` complies to code that produces and returns the model it was originally called on. Thus ``quote`` serves as syntactic sugar for model constructors:: + (for [x [1 2 3]] + (print x) + (when (= x 2) + (break)) + (else (print "loop finished"))) - (quote a) - ; Equivalent to: (hy.models.Symbol "a") - (quote (+ 1 1)) - ; Equivalent to: (hy.models.Expression [ - ; (hy.models.Symbol "+") - ; (hy.models.Integer 1) - ; (hy.models.Integer 1)]) +.. hy:macro:: (lfor [#* args]) - ``quote`` itself is conveniently :ref:`abbreviated ` as the single-quote character ``'``, which needs no parentheses, allowing one to instead write:: + The comprehension forms ``lfor``, :hy:func:`sfor`, :hy:func:`dfor`, :hy:func:`gfor`, and :hy:func:`for` + are used to produce various kinds of loops, including Python-style + :ref:`comprehensions `. ``lfor`` in particular + can create a list comprehension. A simple use of ``lfor`` is:: - 'a - '(+ 1 1) + (lfor x (range 5) (* 2 x)) ; => [0 2 4 6 8] - See also: + ``x`` is the name of a new variable, which is bound to each element of + ``(range 5)``. Each such element in turn is used to evaluate the value + form ``(* 2 x)``, and the results are accumulated into a list. - - :hy:func:`quasiquote` to substitute values into a quoted form - - :hy:func:`hy.eval` to evaluate models as code - - :hy:func:`hy.repr` to stringify models into Hy source text that uses ``'`` + Here's a more complex example:: -.. hy:macro:: (require [#* args]) + (lfor + x (range 3) + y (range 3) + :if (!= x y) + :setv total (+ x y) + [x y total]) + ; => [[0 1 1] [0 2 2] [1 0 1] [1 2 3] [2 0 2] [2 1 3]] - ``require`` is used to import macros and reader macros from one or more given - modules. It allows parameters in all the same formats as ``import``. - ``require`` imports each named module and then makes each requested macro - available in the current module, or in the current local scope if called - locally (using the same notion of locality as :hy:func:`defmacro`). + When there are several iteration clauses (here, the pairs of forms ``x + (range 3)`` and ``y (range 3)``), the result works like a nested loop or + Cartesian product: all combinations are considered in lexicographic + order. - The following are all equivalent ways to call a macro named ``foo`` in the - module ``mymodule``. + The general form of ``lfor`` is:: - :strong:`Examples` + (lfor CLAUSES VALUE) - :: + where the ``VALUE`` is an arbitrary form that is evaluated to produce + each element of the result list, and ``CLAUSES`` is any number of + clauses. There are several types of clauses: - (require mymodule) - (mymodule.foo 1) + - Iteration clauses, which look like ``LVALUE ITERABLE``. The ``LVALUE`` + is usually just a symbol, but could be something more complicated, + like ``[x y]``. + - ``:async LVALUE ITERABLE``, which is an asynchronous form of + iteration clause per Python's :py:keyword:`async for`. + - ``:do FORM``, which simply evaluates the ``FORM``. If you use + ``(continue)`` or ``(break)`` here, it will apply to the innermost + iteration clause before the ``:do``. + - ``:setv LVALUE RVALUE``, which is equivalent to ``:do (setv LVALUE + RVALUE)``. + - ``:if CONDITION``, which is equivalent to ``:do (when (not CONDITION) + (continue))``. - (require mymodule :as M) - (M.foo 1) + For ``lfor``, ``sfor``, ``gfor``, and ``dfor``, variables defined by + an iteration clause or ``:setv`` are not visible outside the form. + However, variables defined within the body, as with a ``setx`` + expression, will be visible outside the form. + In ``for``, by contrast, iteration and ``:setv`` clauses share the + caller's scope and are visible outside the form. - (require mymodule [foo]) - (foo 1) +.. hy:macro:: (dfor [#* args]) - (require mymodule *) - (foo 1) + ``dfor`` creates a :ref:`dictionary comprehension `. Its syntax + is the same as that of :hy:func:`lfor` except that it takes two trailing + arguments. The first is a form producing the key of each dictionary + element, and the second produces the value. Thus:: - (require mymodule [foo :as bar]) - (bar 1) + => (dfor x (range 5) x (* x 10)) + {0 0 1 10 2 20 3 30 4 40} - Reader macros are required using ``:readers [...]``. - The ``:macros`` kwarg can be optionally added for readability:: - => (require mymodule :readers *) - => (require mymodule :readers [!]) - => (require mymodule [foo] :readers [!]) - => (require mymodule :readers [!] [foo]) - => (require mymodule :macros [foo] :readers [!]) +.. hy:macro:: (gfor [#* args]) - Do note however, that requiring ``:readers``, but not specifying any regular - macros, will not bring that module's macros in under their absolute paths:: + ``gfor`` creates a :ref:`generator expression `. Its syntax + is the same as that of :hy:func:`lfor`. The difference is that ``gfor`` returns + an iterator, which evaluates and yields values one at a time:: - => (require mymodule :readers [!]) - => (mymodule.foo) - Traceback (most recent call last): - File "stdin-cd49eaaabebc174c87ebe6bf15f2f8a28660feba", line 1, in - (mymodule.foo) - NameError: name 'mymodule' is not defined + => (import itertools [count take-while]) + => (setv accum []) + => (list (take-while + ... (fn [x] (< x 5)) + ... (gfor x (count) :do (.append accum x) x))) + [0 1 2 3 4] + => accum + [0 1 2 3 4 5] - Unlike requiring regular macros, reader macros cannot be renamed - with ``:as``, are not made available under their absolute paths - to their source module, and can't be required locally:: +.. hy:macro:: (sfor [#* args]) - => (require mymodule :readers [!]) - HySyntaxError: ... + ``sfor`` creates a :ref:`set comprehension `. ``(sfor CLAUSES VALUE)`` is + equivalent to ``(set (lfor CLAUSES VALUE))``. See :hy:func:`lfor`. - => (require mymodule :readers [! :as &]) - HySyntaxError: ... +Context managers and pattern-matching +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - => (require mymodule) - => mymodule.! x - NameError: name 'mymodule' is not defined +.. hy:macro:: (with [managers #* body]) - To define which macros are collected by ``(require mymodule *)``, set the - variable ``_hy_export_macros`` (analogous to Python's ``__all__``) to a list - of :ref:`mangled ` macro names, which is accomplished most - conveniently with :hy:func:`export `. The default - behavior is to collect all macros other than those whose mangled names begin - with an ASCII underscore (``_``). + ``with`` compiles to a :py:keyword:`with` or an :py:keyword:`async with` + statement, which wraps some code with one or more :ref:`context managers + `. The first argument is a bracketed list of context + managers, and the remaining arguments are body forms. - When requiring reader macros, ``(require mymodule :readers *)`` will collect - all reader macros both defined and required within ``mymodule``. + The manager list can't be empty. If it has only one item, that item is + evaluated to obtain the context manager to use. If it has two, the first + argument (a symbol) is bound to the result of the second. Thus, ``(with + [(f)] …)`` compiles to ``with f(): …`` and ``(with [x (f)] …)`` compiles to + ``with f() as x: …``. :: - :strong:`Macros that call macros` + (with [o (open "file.txt" "rt")] + (print (.read o))) - One aspect of ``require`` that may be surprising is what happens when one - macro's expansion calls another macro. Suppose ``mymodule.hy`` looks like this: + If the manager list has more than two items, they're understood as + variable-manager pairs; thus :: - :: + (with [v1 e1 v2 e2 v3 e3] ...) - (defmacro repexpr [n expr] - ; Evaluate the expression n times - ; and collect the results in a list. - `(list (map (fn [_] ~expr) (range ~n)))) + compiles to - (defmacro foo [n] - `(repexpr ~n (input "Gimme some input: "))) + .. code-block:: python - And then, in your main program, you write: + with e1 as v1, e2 as v2, e3 as v3: ... - :: + The symbol ``_`` is interpreted specially as a variable name in the manager + list: instead of binding the context manager to the variable ``_`` (as + Python's ``with e1 as _: …``), ``with`` will leave it anonymous (as Python's + ``with e1: …``). - (require mymodule [foo]) + Finally, any variable-manager pair may be preceded with the keyword + ``:async`` to use an asynchronous context manager:: - (print (mymodule.foo 3)) + (with [:async v1 e1] …) - Running this raises ``NameError: name 'repexpr' is not defined``, even though - writing ``(print (foo 3))`` in ``mymodule`` works fine. The trouble is that your - main program doesn't have the macro ``repexpr`` available, since it wasn't - imported (and imported under exactly that name, as opposed to a qualified name). - You could do ``(require mymodule *)`` or ``(require mymodule [foo repexpr])``, - but a less error-prone approach is to change the definition of - ``foo`` to require whatever sub-macros it needs: + ``with`` returns the value of its last form, unless it suppresses an + exception (because the context manager's ``__exit__`` method returned true), + in which case it returns ``None``. So, the first example could also be + written :: - :: + (print (with [o (open "file.txt" "rt")] (.read o))) - (defmacro foo [n] - `(do - (require mymodule) - (mymodule.repexpr ~n (input "Gimme some input: ")))) +.. hy:macro:: (match [subject #* cases]) - It's wise to use ``(require mymodule)`` here rather than ``(require mymodule - [repexpr])`` to avoid accidentally shadowing a function named ``repexpr`` in - the main program. + The ``match`` form creates a :ref:`match statement `. It + requires Python 3.10 or later. The first argument should be the subject, + and any remaining arguments should be pairs of patterns and results. The + ``match`` form returns the value of the corresponding result, or + ``None`` if no case matched. :: - .. note:: + (match (+ 1 1) + 1 "one" + 2 "two" + 3 "three") + ; => "two" - :strong:`Qualified macro names` + You can use :hy:func:`do` to build a complex result form. Patterns, as + in Python match statements, are interpreted specially and can't be + arbitrary forms. Use ``(| …)`` for OR patterns, ``PATTERN :as NAME`` for + AS patterns, and syntax like the usual Hy syntax for literal, capture, + value, sequence, mapping, and class patterns. Guards are specified + with ``:if FORM``. Here's a more complex example:: - Note that in the current implementation, there's a trick in qualified macro - names, like ``mymodule.foo`` and ``M.foo`` in the above example. These names - aren't actually attributes of module objects; they're just identifiers with - periods in them. In fact, ``mymodule`` and ``M`` aren't defined by these - ``require`` forms, even at compile-time. None of this will hurt you unless try - to do introspection of the current module's set of defined macros, which isn't - really supported anyway. + (match #(100 200) + [100 300] "Case 1" + [100 200] :if flag "Case 2" + [900 y] f"Case 3, y: {y}" + [100 (| 100 200) :as y] f"Case 4, y: {y}" + _ "Case 5, I match anything!") -.. hy:macro:: (return [object]) + This will match case 2 if ``flag`` is true and case 4 otherwise. - ``return`` compiles to a :py:keyword:`return` statement. It exits the - current function, returning its argument if provided with one, or - ``None`` if not. :: + ``match`` can also match against class instances by keyword (or + positionally if its ``__match_args__`` attribute is defined; see :pep:`636`):: - (defn f [x] - (for [n (range 10)] - (when (> n x) - (return n)))) - (f 3.9) ; => 4 + (import dataclasses [dataclass]) + (defclass [dataclass] Point [] + #^ int x + #^ int y) + (match (Point 1 2) + (Point 1 x) :if (= (% x 2) 0) x) ; => 2 - Note that in Hy, ``return`` is necessary much less often than in Python. - The last form of a function is returned automatically, so an - explicit ``return`` is only necessary to exit a function early. To force - Python's behavior of returning ``None`` when execution reaches the end of a - function, just put ``None`` there yourself:: + It's worth emphasizing that ``match`` is a pattern-matching construct + rather than a generic `switch + `_ construct, and + retains all of Python's limitations on match patterns. For example, you + can't match against the value of a variable. For more flexible branching + constructs, see Hyrule's :hy:func:`branch ` and + :hy:func:`case `, or simply use :hy:func:`cond + `. - (defn f [] - (setv d (dict :a 1 :b 2)) - (.pop d "b") - None) - (print (f)) ; Prints "None", not "2" +Exception-handling +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. hy:macro:: (raise [exception :from other]) @@ -1107,197 +915,440 @@ base names, such that ``hy.core.macros.foo`` can be called as just ``foo``. The return value of ``try`` is the last form evaluated among the main body, ``except`` forms, ``except*`` forms, and ``else``. -.. hy:macro:: (unpack-iterable [form]) -.. hy:macro:: (unpack-mapping [form]) +Functions +~~~~~~~~~ - (Also known as the splat operator, star operator, argument expansion, argument - explosion, argument gathering, and varargs, among others...) +.. hy:macro:: (defn [name #* args]) - ``unpack-iterable`` and ``unpack-mapping`` allow an iterable or mapping - object (respectively) to provide positional or keywords arguments - (respectively) to a function. + ``defn`` compiles to a :ref:`function definition ` (or possibly + to an assignment of a :ref:`lambda expression `). It always + returns ``None``. It requires two arguments: a name (given as a symbol; see + :hy:func:`fn` for anonymous functions) and a "lambda list", or list of + parameters (also given as symbols). Any further arguments constitute the + body of the function:: + + (defn name [params] bodyform1 bodyform2…) + + An empty body is implicitly ``(return None)``. If there are at least two body + forms, and the first of them is a string literal, this string becomes the + :term:`py:docstring` of the function. The final body form is implicitly + returned; thus, ``(defn f [] 5)`` is equivalent to ``(defn f [] (return + 5))``. There is one exception: due to Python limitations, no implicit return + is added if the function is an asynchronous generator (i.e., defined with + ``(defn :async …)`` or ``(fn :async …)`` and containing at least one + :hy:func:`yield`). + + ``defn`` accepts a few more optional arguments: a literal keyword ``:async`` + (to create a :ref:`coroutine ` like Python's ``async def``), a + bracketed list of :term:`decorators `, a list of type + parameters (see below), and an annotation (see :hy:func:`annotate`) for the + return value. These are placed before the function name (in that order, if + several are present):: + + (defn :async [decorator1 decorator2] :tp [T1 T2] #^ annotation name [params] …) + + ``defn`` lambda lists support all the same features as Python parameter + lists and hence are complex in their full generality. The simplest case is a + (possibly empty) list of symbols, indicating that all parameters are + required, and can be set by position, as in ``(f value)``, or by name, as in + ``(f :argument value)``. To set a default value for a parameter, replace the + parameter with the bracketed list ``[pname value]``, where ``pname`` is the + parameter name as a symbol and ``value`` is an arbitrary form. Beware that, + per Python, ``value`` is evaluated when the function is defined, not when + it's called, and if the resulting object is mutated, all calls will see the + changes. + + Further special lambda-list syntax includes: + + ``/`` + If the symbol ``/`` is given in place of a parameter, it means that all + the preceding parameters can only be set positionally. + + ``*`` + If the symbol ``*`` is given in place of a parameter, it means that all + the following parameters can only be set by name. + + ``#* args`` + If the parameter list contains ``#* args`` or ``(unpack-iterable + args)``, then ``args`` is set to a tuple containing all otherwise + unmatched positional arguments. The name ``args`` is merely cherished + Python tradition; you can use any symbol. + + ``#** kwargs`` + ``#** kwargs`` (a.k.a. ``(unpack-mapping kwargs)``) is like ``#* + args``, but collects unmatched keyword arguments into a dictionary. + + Each of these special constructs is allowed only once, and has the same + restrictions as in Python; e.g., ``#* args`` must precede ``#** kwargs`` if + both are present. Here's an example with a complex lambda list:: + + (defn f [a / b [c 3] * d e #** kwargs] + [a b c d e kwargs]) + (print (hy.repr (f 1 2 :d 4 :e 5 :f 6))) + ; => [1 2 3 4 5 {"f" 6}] + + Type parameters require Python 3.12, and have the semantics specified by + :pep:`695`. The keyword ``:tp`` introduces the list of type parameters. Each + item of the list is a symbol, an annotated symbol (such as ``#^ int T``), or + an unpacked symbol (such as ``#* T`` or ``#** T``). As in Python, unpacking + and annotation can't be used with the same parameter. + +.. hy:macro:: (fn [args]) + + As :hy:func:`defn`, but no name for the new function is required (or + allowed), and the newly created function object is returned. Decorators and + type parameters aren't allowed, either. However, the function body is + understood identically to that of :hy:func:`defn`, without any of the + restrictions of Python's :py:keyword:`lambda`. ``:async`` is also allowed. + +.. hy:macro:: (return [object]) + + ``return`` compiles to a :py:keyword:`return` statement. It exits the + current function, returning its argument if provided with one, or + ``None`` if not. :: + + (defn f [x] + (for [n (range 10)] + (when (> n x) + (return n)))) + (f 3.9) ; => 4 + + Note that in Hy, ``return`` is necessary much less often than in Python. + The last form of a function is returned automatically, so an + explicit ``return`` is only necessary to exit a function early. To force + Python's behavior of returning ``None`` when execution reaches the end of a + function, just put ``None`` there yourself:: + + (defn f [] + (setv d (dict :a 1 :b 2)) + (.pop d "b") + None) + (print (f)) ; Prints "None", not "2" + +.. hy:macro:: (yield [arg1 arg2]) + + ``yield`` compiles to a :ref:`yield expression `, which + returns a value as a generator. For a plain yield, provide one argument, + the value to yield, or omit it to yield ``None``. :: + + (defn naysayer [] + (while True + (yield "nope"))) + (hy.repr (list (zip "abc" (naysayer)))) + ; => [#("a" "nope") #("b" "nope") #("c" "nope")] + + For a yield-from expression, provide two arguments, where the first is the + literal keyword ``:from`` and the second is the subgenerator. :: + + (defn myrange [] + (setv r (range 10)) + (while True + (yield :from r))) + (hy.repr (list (zip "abc" (myrange)))) + ; => [#("a" 0) #("b" 1) #("c" 2)] + +.. hy:macro:: (await [obj]) + + ``await`` creates an :ref:`await expression `. It takes exactly one + argument: the object to wait for. :: + + (import asyncio) + (defn :async main [] + (print "hello") + (await (asyncio.sleep 1)) + (print "world")) + (asyncio.run (main)) + +Macros +~~~~~~~~~~~~ + +.. hy:macro:: (defmacro [name lambda-list #* body]) + + ``defmacro`` is used to define macros. The general format is + ``(defmacro name [parameters] expr)``. + + The following example defines a macro that can be used to swap order of elements + in code, allowing the user to write code in infix notation, where operator is in + between the operands. + + :strong:`Examples` + :: + + => (defmacro infix [code] + ... (quasiquote ( + ... (unquote (get code 1)) + ... (unquote (get code 0)) + ... (unquote (get code 2))))) :: - => (defn f [a b c d] [a b c d]) - => (f (unpack-iterable [1 2]) (unpack-mapping {"c" 3 "d" 4})) - [1 2 3 4] + => (infix (1 + 1)) + 2 + + If ``defmacro`` appears in a function definition, a class definition, or a + comprehension other than :hy:func:`for` (such as :hy:func:`lfor`), the new + macro is defined locally rather than module-wide. + + .. note:: ``defmacro`` cannot use keyword arguments, because all values + are passed to macros unevaluated. All arguments are passed + positionally, but they can have default values:: + + => (defmacro a-macro [a [b 1]] + ... `[~a ~b]) + => (a-macro 2) + [2 1] + => (a-macro 2 3) + [2 3] + => (a-macro :b 3) + [:b 3] + +.. hy:automacro:: hy.core.macros.defreader + +.. hy:automacro:: hy.core.macros.get-macro + +.. hy:automacro:: hy.core.macros.local-macros + +Classes +~~~~~~~~~~~~ + +.. hy:macro:: (defclass [arg1 #* args]) + + ``defclass`` compiles to a :py:keyword:`class` statement, which creates a + new class. It always returns ``None``. Only one argument, specifying the + name of the new class as a symbol, is required. A list of :term:`decorators + ` (and type parameters, in the same way as for + :hy:func:`defn`) may be provided before the class name. After the name comes + a list of superclasses (use the empty list ``[]`` for the typical case of no + superclasses) and any number of body forms, the first of which may be a + :term:`py:docstring`. :: + + (defclass [decorator1 decorator2] :tp [T1 T2] MyClass [SuperClass1 SuperClass2] + "A class that does things at times." + + (setv + attribute1 value1 + attribute2 value2) + + (defn method1 [self arg1 arg2] + …) + + (defn method2 [self arg1 arg2] + …)) + +Modules +~~~~~~~~~~~~ + +.. hy:macro:: (import [#* forms]) + + ``import`` compiles to an :py:keyword:`import` statement, which makes objects + in a different module available in the current module. It always returns + ``None``. Hy's syntax for the various kinds of import looks like this:: + + ;; Import each of these modules + ;; Python: import sys, os.path + (import sys os.path) + + ;; Import several names from a single module + ;; Python: from os.path import exists, isdir as is_dir, isfile + (import os.path [exists isdir :as dir? isfile]) + + ;; Import with an alias + ;; Python: import sys as systest + (import sys :as systest) + + ;; You can list as many imports as you like of different types. + ;; Python: + ;; from tests.resources import kwtest, function_with_a_dash + ;; from os.path import exists, isdir as is_dir, isfile as is_file + ;; import sys as systest + (import tests.resources [kwtest function-with-a-dash] + os.path [exists + isdir :as dir? + isfile :as file?] + sys :as systest) + + ;; Import all module functions into current namespace + ;; Python: from sys import * + (import sys *) + + ``__all__`` can be set to control what's imported by ``import *``, as in + Python, but beware that all names in ``__all__`` must be :ref:`mangled + `. The macro :hy:func:`export ` is a handy + way to set ``__all__`` in a Hy program. - ``unpack-iterable`` is usually written with the shorthand ``#*``, and - ``unpack-mapping`` with ``#**``. +.. hy:macro:: (require [#* args]) - :: + ``require`` is used to import macros and reader macros from one or more given + modules. It allows parameters in all the same formats as ``import``. + ``require`` imports each named module and then makes each requested macro + available in the current module, or in the current local scope if called + locally (using the same notion of locality as :hy:func:`defmacro`). - => (f #* [1 2] #** {"c" 3 "d" 4}) - [1 2 3 4] + The following are all equivalent ways to call a macro named ``foo`` in the + module ``mymodule``. - Unpacking is allowed in a variety of contexts, and you can unpack - more than once in one expression (:pep:`3132`, :pep:`448`). + :strong:`Examples` :: - => (setv [a #* b c] [1 2 3 4 5]) - => [a b c] - [1 [2 3 4] 5] - => [#* [1 2] #* [3 4]] - [1 2 3 4] - => {#** {1 2} #** {3 4}} - {1 2 3 4} - => (f #* [1] #* [2] #** {"c" 3} #** {"d" 4}) - [1 2 3 4] + (require mymodule) + (mymodule.foo 1) -.. hy:macro:: (while [condition #* body]) + (require mymodule :as M) + (M.foo 1) - ``while`` compiles to a :py:keyword:`while` statement, which executes some - code as long as a condition is met. The first argument to ``while`` is the - condition, and any remaining forms constitute the body. It always returns - ``None``. :: + (require mymodule [foo]) + (foo 1) - (while True - (print "Hello world!")) + (require mymodule *) + (foo 1) - The last form of a ``while`` loop can be an ``else`` clause, which is - executed after the loop terminates, unless it exited abnormally (e.g., with - ``break``). So, :: + (require mymodule [foo :as bar]) + (bar 1) - (setv x 2) - (while x - (print "In body") - (-= x 1) - (else - (print "In else"))) + Reader macros are required using ``:readers [...]``. + The ``:macros`` kwarg can be optionally added for readability:: - prints :: + => (require mymodule :readers *) + => (require mymodule :readers [!]) + => (require mymodule [foo] :readers [!]) + => (require mymodule :readers [!] [foo]) + => (require mymodule :macros [foo] :readers [!]) - In body - In body - In else + Do note however, that requiring ``:readers``, but not specifying any regular + macros, will not bring that module's macros in under their absolute paths:: - If you put a ``break`` or ``continue`` form in the condition of a ``while`` - loop, it will apply to the very same loop rather than an outer loop, even if - execution is yet to ever reach the loop body. (Hy compiles a ``while`` loop - with statements in its condition by rewriting it so that the condition is - actually in the body.) So, :: + => (require mymodule :readers [!]) + => (mymodule.foo) + Traceback (most recent call last): + File "stdin-cd49eaaabebc174c87ebe6bf15f2f8a28660feba", line 1, in + (mymodule.foo) + NameError: name 'mymodule' is not defined - (for [x [1]] - (print "In outer loop") - (while - (do - (print "In condition") - (break) - (print "This won't print.") - True) - (print "This won't print, either.")) - (print "At end of outer loop")) + Unlike requiring regular macros, reader macros cannot be renamed + with ``:as``, are not made available under their absolute paths + to their source module, and can't be required locally:: - prints :: + => (require mymodule :readers [!]) + HySyntaxError: ... - In outer loop - In condition - At end of outer loop + => (require mymodule :readers [! :as &]) + HySyntaxError: ... -.. hy:macro:: (with [managers #* body]) + => (require mymodule) + => mymodule.! x + NameError: name 'mymodule' is not defined - ``with`` compiles to a :py:keyword:`with` or an :py:keyword:`async with` - statement, which wraps some code with one or more :ref:`context managers - `. The first argument is a bracketed list of context - managers, and the remaining arguments are body forms. + To define which macros are collected by ``(require mymodule *)``, set the + variable ``_hy_export_macros`` (analogous to Python's ``__all__``) to a list + of :ref:`mangled ` macro names, which is accomplished most + conveniently with :hy:func:`export `. The default + behavior is to collect all macros other than those whose mangled names begin + with an ASCII underscore (``_``). - The manager list can't be empty. If it has only one item, that item is - evaluated to obtain the context manager to use. If it has two, the first - argument (a symbol) is bound to the result of the second. Thus, ``(with - [(f)] …)`` compiles to ``with f(): …`` and ``(with [x (f)] …)`` compiles to - ``with f() as x: …``. :: + When requiring reader macros, ``(require mymodule :readers *)`` will collect + all reader macros both defined and required within ``mymodule``. - (with [o (open "file.txt" "rt")] - (print (.read o))) + :strong:`Macros that call macros` - If the manager list has more than two items, they're understood as - variable-manager pairs; thus :: + One aspect of ``require`` that may be surprising is what happens when one + macro's expansion calls another macro. Suppose ``mymodule.hy`` looks like this: - (with [v1 e1 v2 e2 v3 e3] ...) + :: - compiles to + (defmacro repexpr [n expr] + ; Evaluate the expression n times + ; and collect the results in a list. + `(list (map (fn [_] ~expr) (range ~n)))) - .. code-block:: python + (defmacro foo [n] + `(repexpr ~n (input "Gimme some input: "))) - with e1 as v1, e2 as v2, e3 as v3: ... + And then, in your main program, you write: - The symbol ``_`` is interpreted specially as a variable name in the manager - list: instead of binding the context manager to the variable ``_`` (as - Python's ``with e1 as _: …``), ``with`` will leave it anonymous (as Python's - ``with e1: …``). + :: - Finally, any variable-manager pair may be preceded with the keyword - ``:async`` to use an asynchronous context manager:: + (require mymodule [foo]) - (with [:async v1 e1] …) + (print (mymodule.foo 3)) - ``with`` returns the value of its last form, unless it suppresses an - exception (because the context manager's ``__exit__`` method returned true), - in which case it returns ``None``. So, the first example could also be - written :: + Running this raises ``NameError: name 'repexpr' is not defined``, even though + writing ``(print (foo 3))`` in ``mymodule`` works fine. The trouble is that your + main program doesn't have the macro ``repexpr`` available, since it wasn't + imported (and imported under exactly that name, as opposed to a qualified name). + You could do ``(require mymodule *)`` or ``(require mymodule [foo repexpr])``, + but a less error-prone approach is to change the definition of + ``foo`` to require whatever sub-macros it needs: - (print (with [o (open "file.txt" "rt")] (.read o))) + :: -.. hy:macro:: (yield [arg1 arg2]) + (defmacro foo [n] + `(do + (require mymodule) + (mymodule.repexpr ~n (input "Gimme some input: ")))) - ``yield`` compiles to a :ref:`yield expression `, which - returns a value as a generator. For a plain yield, provide one argument, - the value to yield, or omit it to yield ``None``. :: + It's wise to use ``(require mymodule)`` here rather than ``(require mymodule + [repexpr])`` to avoid accidentally shadowing a function named ``repexpr`` in + the main program. - (defn naysayer [] - (while True - (yield "nope"))) - (hy.repr (list (zip "abc" (naysayer)))) - ; => [#("a" "nope") #("b" "nope") #("c" "nope")] + .. note:: - For a yield-from expression, provide two arguments, where the first is the - literal keyword ``:from`` and the second is the subgenerator. :: + :strong:`Qualified macro names` - (defn myrange [] - (setv r (range 10)) - (while True - (yield :from r))) - (hy.repr (list (zip "abc" (myrange)))) - ; => [#("a" 0) #("b" 1) #("c" 2)] + Note that in the current implementation, there's a trick in qualified macro + names, like ``mymodule.foo`` and ``M.foo`` in the above example. These names + aren't actually attributes of module objects; they're just identifiers with + periods in them. In fact, ``mymodule`` and ``M`` aren't defined by these + ``require`` forms, even at compile-time. None of this will hurt you unless try + to do introspection of the current module's set of defined macros, which isn't + really supported anyway. -.. hy:macro:: (deftype [args]) +.. hy:automacro:: hy.core.macros.export - ``deftype`` compiles to a :py:keyword:`type` statement, which defines a - type alias. It requires Python 3.12. Its arguments optionally begin with - ``:tp`` and a list of type parameters (as in :hy:func:`defn`), then specify - the name for the new alias and its value. :: +Miscellany +~~~~~~~~~~~~ - (deftype IntOrStr (| int str)) - (deftype :tp [T] ListOrSet (| (get list T) (get set T))) +.. hy:macro:: (chainc [#* args]) -.. hy:macro:: (pragma [#* args]) + ``chainc`` creates a :ref:`comparison expression `. It isn't + required for unchained comparisons, which have only one comparison operator, + nor for chains of the same operator. For those cases, you can use the + comparison operators directly with Hy's usual prefix syntax, as in ``(= x 1)`` + or ``(< 1 2 3)``. The use of ``chainc`` is to construct chains of + heterogeneous operators, such as ``x <= y < z``. It uses an infix syntax with + the general form - ``pragma`` is used to adjust the state of the compiler. It's called for its - side-effects, and returns ``None``. The arguments are key-value pairs, like a - function call with keyword arguments:: + :: - (pragma :prag1 value1 :prag2 (get-value2)) + (chainc ARG OP ARG OP ARG…) - Each key is a literal keyword giving the name of a pragma. Each value is an - arbitrary form, which is evaluated as ordinary Hy code but at compile-time. + Hence, ``(chainc x <= y < z)`` is equivalent to ``(and (<= x y) (< y z))``, + including short-circuiting, except that ``y`` is only evaluated once. - The effect of each pragma is locally scoped to its containing function, - class, or comprehension form (other than ``for``), if there is one. + Each ``ARG`` is an arbitrary form, which does not itself use infix syntax. Use + :hy:func:`py ` if you want fully Python-style operator syntax. You can + also nest ``chainc`` forms, although this is rarely useful. Each ``OP`` is a + literal comparison operator; other forms that resolve to a comparison operator + are not allowed. - Only one pragma is currently implemented: + At least two ``ARG``\ s and one ``OP`` are required, and every ``OP`` must be + followed by an ``ARG``. -.. _warn-on-core-shadow: + As elsewhere in Hy, the equality operator is spelled ``=``, not ``==`` as in + Python. - - ``:warn-on-core-shadow``: If true (the default), :hy:func:`defmacro` and - :hy:func:`require` will raise a warning at compile-time if you define a macro - with the same name as a core macro. Shadowing a core macro in this fashion is - dangerous, because other macros may call your new macro when they meant to - refer to the core macro. +.. hy:macro:: (assert [condition [label None]]) + + ``assert`` compiles to an :py:keyword:`assert` statement, which checks + whether a condition is true. The first argument, specifying the condition to + check, is mandatory, whereas the second, which will be passed to + :py:class:`AssertionError`, is optional. The whole form is only evaluated + when :py:data:`__debug__` is true, and the second argument is only evaluated + when :py:data:`__debug__` is true and the condition fails. ``assert`` always + returns ``None``. :: -.. hy:automodule:: hy.core.macros - :macros: + (assert (= 1 2) "one should equal two") + ; AssertionError: one should equal two Placeholder macros ~~~~~~~~~~~~~~~~~~ @@ -1357,7 +1408,7 @@ the following methods There is no actual object named ``hy.R``. Rather, this syntax is :ref:`recognized specially by the compiler ` as a shorthand for requiring and calling a macro. -Reader Macros +Reader macros ------------- .. autoclass:: hy.reader.hy_reader.HyReader @@ -1368,7 +1419,9 @@ Reader Macros .. autoexception:: hy.reader.exceptions.PrematureEndOfInput -Python Operators +.. _pyop: + +Python operators ---------------- .. hy:automodule:: hy.pyops diff --git a/docs/cli.rst b/docs/cli.rst index 5ef16b5f3..2f2afdc9e 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -1,5 +1,5 @@ ====================== -Command-Line Interface +Command-line interface ====================== Hy provides a handful of command-line programs for working with Hy code. diff --git a/docs/env_var.rst b/docs/env_var.rst index 202911153..baa427a00 100644 --- a/docs/env_var.rst +++ b/docs/env_var.rst @@ -1,5 +1,5 @@ ===================== -Environment Variables +Environment variables ===================== Hy treats the following environment variables specially. Boolean environment diff --git a/docs/hacking.rst b/docs/hacking.rst index 9a10daee9..8f6fcc5cc 100644 --- a/docs/hacking.rst +++ b/docs/hacking.rst @@ -9,7 +9,7 @@ .. include:: ../CONTRIBUTING.rst -Core Team +Core team ========= Hy's core development team consists of the following people: diff --git a/docs/index.rst b/docs/index.rst index ddbbfc452..f28d6fe47 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -32,10 +32,10 @@ Linux, Windows, and Mac OS), and on recent versions of PyPy and Pyodide. syntax semantics macros + model_patterns repl env_var cli interop - model_patterns api hacking diff --git a/docs/interop.rst b/docs/interop.rst index 955cd7917..028311206 100644 --- a/docs/interop.rst +++ b/docs/interop.rst @@ -1,17 +1,52 @@ .. _interop: ======================= -Python Interoperability +Python interoperability ======================= +.. contents:: Contents + :local: + +Mangling +======== + :ref:`Mangling ` allows variable names to be spelled differently in Hy and Python. For example, Python's ``str.format_map`` can be written ``str.format-map`` in Hy, and a Hy function named ``valid?`` would be called ``hyx_valid_Xquestion_markX`` in Python. You can call :hy:func:`hy.mangle` and :hy:func:`hy.unmangle` from either language. -.. contents:: Contents - :local: +Libraries that expect Python +============================ + +There are various means by which Hy may interact poorly with a Python library because the library doesn't account for the possibility of Hy. For example, +when you run :ref:`hy-cli`, ``sys.executable`` will be set to +this program rather than the original Python binary. This is helpful more often +than not, but will lead to trouble if e.g. the library tries to call +:py:data:`sys.executable` with the ``-c`` option. In this case, you can try +setting :py:data:`sys.executable` back to ``hy.sys-executable``, which is a +saved copy of the original value. More generally, you can use :ref:`hy2py`, or you +can put a simple Python wrapper script like ``import hy, my_hy_program`` in +front of your code. + +See `the wiki +`_ for tips +on using specific packages. + +Packaging a Hy library +====================== + +Generally, the same infrastructure used for Python packages, such as +``setup.py`` files and the `Python Package Index (PyPI) `__, +is applicable to Hy. Don't write the setup file itself in Hy, since you'll be +declaring your package's dependence on Hy there, likely in the +``install_requires`` argument of ``setup``. See :ref:`using-hy-from-python` +below for some related issues to keep in mind. + +If you want to compile your Hy code into Python bytecode at installation-time +(in case e.g. the code is being installed to a directory where the bytecode be +able to be automatically written later, due to permissions issues), see Hy's +own ``setup.py`` for an example. Using Python from Hy ==================== @@ -24,6 +59,8 @@ You can embed Python code directly into a Hy program with the macros tools like :func:`eval` or :func:`exec` to execute or manipulate Python code in strings. +.. _using-hy-from-python: + Using Hy from Python ==================== @@ -54,17 +91,4 @@ There is no Hy equivalent of :func:`exec` because :hy:func:`hy.eval` works even when the input isn't equivalent to a single Python expression. You can use :meth:`hy.REPL.run` to launch the Hy REPL from Python, as in -``hy.REPL(locals = locals()).run()``. - -Libraries that expect Python -============================ - -There are various means by which Hy may interact poorly with a Python library because the library doesn't account for the possibility of Hy. For example, -when you run :ref:`hy-cli`, ``sys.executable`` will be set to -this program rather than the original Python binary. This is helpful more often -than not, but will lead to trouble if e.g. the library tries to call -:py:data:`sys.executable` with the ``-c`` option. In this case, you can try -setting :py:data:`sys.executable` back to ``hy.sys-executable``, which is a -saved copy of the original value. More generally, you can use :ref:`hy2py`, or you -can put a simple Python wrapper script like ``import hy, my_hy_program`` in -front of your code. +``hy.REPL(locals = {**globals(), **locals()}).run()``. diff --git a/docs/model_patterns.rst b/docs/model_patterns.rst index 5619fe995..c7fe346d1 100644 --- a/docs/model_patterns.rst +++ b/docs/model_patterns.rst @@ -1,5 +1,5 @@ ============== -Model Patterns +Model patterns ============== The module ``hy.model-patterns`` provides a library of parser combinators for diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 86909504d..8f936f64d 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -354,8 +354,8 @@ like so:: ``require`` can pull in a reader macro defined in a different module with syntax like ``(require mymodule :readers [d])``. -Hyrule -====== +Recommended libraries +===================== `Hyrule `_ is Hy's standard utility library. It provides a variety of functions and macros that are useful for writing Hy @@ -364,11 +364,31 @@ programs. :: => (import hyrule [inc]) => (list (map inc [1 2 3])) [2 3 4] - => (require hyrule [assoc]) - => (setv d {}) - => (assoc d "a" 1 "b" 2) - => d - {"a" 1 "b" 2} + => (require hyrule [case]) + => (setv x 2) + => (case x 1 "a" 2 "b" 3 "c") + "b" + +`toolz `_ and its Cython variant `cytoolz +`_ provide lots of utilities for functional +programming and working with iterables. :: + + => (import toolz [partition]) + => (list (partition 2 [1 2 3 4 5 6])) + [#(1 2) #(3 4) #(5 6)] + +`metadict `_ allows you to refer to the +elements of a dictionary as attributes. This is handy when frequently referring +to elements with constant strings as keys, since plain indexing is a bit +verbose in Hy. :: + + => (import metadict [MetaDict]) + => (setv d (MetaDict)) + => (setv d.foo 1) ; i.e., (setv (get d "foo") 1) + => d.foo ; i.e., (get d "foo") + 1 + => (list (.keys d)) + ["foo"] Next steps ========== @@ -384,8 +404,9 @@ understand example code for Python libraries. Refer to the rest of this manual for Hy-specific features. Like Hy itself, the manual is incomplete, but :ref:`contributions ` are always welcome. -For an official full-blown example Hy program, see `Infinitesimal Quest 2 + ε -`_. +See `the wiki `_ for tips +on getting Hy to work with other software. For an official full-blown example +Hy program, see `Infinitesimal Quest 2 + ε `_. Bear in mind that Hy is still unstable, and with each release along the way to Hy 1.0, there are new breaking changes. Refer to `the NEWS file diff --git a/hy/core/result_macros.py b/hy/core/result_macros.py index f43bdabb3..062f97e03 100644 --- a/hy/core/result_macros.py +++ b/hy/core/result_macros.py @@ -162,6 +162,17 @@ def compile_inline_python(compiler, expr, root, code): return Result(stmts=o) if exec_mode else o +@pattern_macro("pragma", [many(KEYWORD + FORM)]) +def compile_pragma(compiler, expr, root, kwargs): + for kw, value in kwargs: + if kw == Keyword("warn-on-core-shadow"): + compiler.local_state_stack[-1]['warn_on_core_shadow'] = ( + bool(compiler.eval(value))) + else: + raise compiler._syntax_error(kw, f"Unknown pragma `{kw}`. Perhaps it's implemented by a newer version of Hy.") + return Result() + + # ------------------------------------------------ # * Quoting # ------------------------------------------------ @@ -470,6 +481,19 @@ def compile_def_expression(compiler, expr, root, decls): return result +@pattern_macro("let", [brackets(many(maybe_annotated(FORM) + FORM)), many(FORM)]) +def compile_let(compiler, expr, root, bindings, body): + res = Result() + bindings = bindings[0] + scope = compiler.scope.create(ScopeLet) + + for (target, ann), value in bindings: + res += compile_assign(compiler, ann, target, value, let_scope=scope) + + with scope: + return res + compiler.compile(mkexpr("do", *body).replace(expr)) + + @pattern_macro(["annotate"], [FORM, FORM]) def compile_basic_annotation(compiler, expr, root, target, ann): return compile_assign(compiler, ann, target, None) @@ -526,6 +550,14 @@ def compile_assign( return result +@pattern_macro(((3, 12), "deftype"), [maybe(type_params), SYM, FORM]) +def compile_deftype(compiler, expr, root, tp, name, value): + return asty.TypeAlias(expr, + name = asty.Name(name, id = mangle(name), ctx = ast.Store()), + value = compiler.compile(value).force_expr, + **digest_type_params(compiler, tp)) + + @pattern_macro(["global", "nonlocal"], [many(SYM)]) def compile_global_or_nonlocal(compiler, expr, root, syms): if not syms: @@ -2003,38 +2035,6 @@ def compile_assert_expression(compiler, expr, root, test, msg): ) -@pattern_macro("let", [brackets(many(maybe_annotated(FORM) + FORM)), many(FORM)]) -def compile_let(compiler, expr, root, bindings, body): - res = Result() - bindings = bindings[0] - scope = compiler.scope.create(ScopeLet) - - for (target, ann), value in bindings: - res += compile_assign(compiler, ann, target, value, let_scope=scope) - - with scope: - return res + compiler.compile(mkexpr("do", *body).replace(expr)) - - -@pattern_macro(((3, 12), "deftype"), [maybe(type_params), SYM, FORM]) -def compile_deftype(compiler, expr, root, tp, name, value): - return asty.TypeAlias(expr, - name = asty.Name(name, id = mangle(name), ctx = ast.Store()), - value = compiler.compile(value).force_expr, - **digest_type_params(compiler, tp)) - - -@pattern_macro("pragma", [many(KEYWORD + FORM)]) -def compile_pragma(compiler, expr, root, kwargs): - for kw, value in kwargs: - if kw == Keyword("warn-on-core-shadow"): - compiler.local_state_stack[-1]['warn_on_core_shadow'] = ( - bool(compiler.eval(value))) - else: - raise compiler._syntax_error(kw, f"Unknown pragma `{kw}`. Perhaps it's implemented by a newer version of Hy.") - return Result() - - @pattern_macro( "unquote unquote-splice unpack-mapping except except* finally else".split(), [many(FORM)],