Skip to content

Commit

Permalink
Merge pull request #2571 from Kodiologist/docs-content
Browse files Browse the repository at this point in the history
Content edits for the manual
  • Loading branch information
Kodiologist authored Apr 27, 2024
2 parents 3d70cb4 + df8cbe6 commit 225017d
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 93 deletions.
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
release = hy_version
# The full version identifier, including alpha, beta, and RC tags

hyrule_version = 'v0.5.0'
hyrule_version = 'doc-testing'

source_suffix = '.rst'
master_doc = 'index'
Expand Down
42 changes: 34 additions & 8 deletions docs/interop.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
Python interoperability
=======================

This chapter describes how to interact with Python code from Hy code and vice
versa.

.. contents:: Contents
:local:

Expand All @@ -16,11 +19,28 @@ Hy and Python. For example, Python's ``str.format_map`` can be written
``hyx_valid_Xquestion_markX`` in Python. You can call :hy:func:`hy.mangle` and
:hy:func:`hy.unmangle` from either language.

Keyword mincing
---------------

Another kind of mangling may be necessary in Python to refer to variables with
the same name as reserved words. For example, while ``(setv break 13)`` is
legal Hy, ``import hy, my_hy_module; print(my_hy_module.break)`` is
syntactically invalid Python. String literals work, as in
``getattr(my_hy_module, "break")``, but to use what is syntactically a Python
identifier, you'll have to take advantage of Python's Unicode normalization
(via NFKC) and write something like ``my_hy_module.𝐛reak``. Here are all the
MATHEMATICAL BOLD SMALL letters (U+1D41A through U+1D433) for convenient
copying:

.. code-block:: text
𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳
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
when you run the program :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
Expand All @@ -44,9 +64,15 @@ declaring your package's dependence on Hy there, likely in the
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.
(in case e.g. the code is being installed to a directory where the bytecode
can't be automatically written later, due to permissions issues), see Hy's own
``setup.py`` for an example.

For PyPI packages, use the trove classifier ``Programming Language :: Hy`` for
libraries meant to be useful for Hy specifically (e.g., a library that provides
Hy macros) or other projects that are about Hy somehow (e.g., an instructive
example Hy program). Don't use it for a package that just happens to be written
in Hy.

Using Python from Hy
====================
Expand All @@ -67,10 +93,10 @@ Using Hy from Python
To use a Hy module from Python, you can just :py:keyword:`import` it, provided
that ``hy`` has already been imported first, whether in the current module or
in some earlier module executed by the current Python process. The ``hy``
import is necessary to create the hooks that allow importing Hy modules. Note
that you can always have a wrapper Python file (such as a package's
``__init__.py``) do the ``import hy`` for the user; this is a smart thing to do
for a published package.
import is necessary to create the hooks that allow importing Hy modules. you
can have a wrapper Python file (such as a package's ``__init__.py``) do the
``import hy`` for the user; this is a smart thing to do for a published
package.

No way to import macros or reader macros into a Python module is implemented,
since there's no way to call them in Python anyway.
Expand Down
8 changes: 5 additions & 3 deletions docs/macros.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ A regular macro can be defined with :hy:func:`defmacro` using a syntax similar t

(print (seventeen))

To see that ``seventeen`` is expanded at compile-time, run ``hy2py`` on this script and notice that it ends with ``print(17)`` rather than ``print(seventeen())``. If you insert a ``print`` call inside the macro definition, you'll also see that the print happens when the file is compiled, but not when it's rerun (provided an up-to-date bytecode file exists).
To see that ``seventeen`` is expanded at compile-time, run ``hy2py`` on this script and notice that it ends with ``print(17)`` rather than ``print(seventeen())``. If you insert a ``print`` call inside the macro definition, you'll also see that the print happens when the file is compiled, but not when it's rerun (so long as an up-to-date bytecode file exists).

A more useful macro returns code. You can construct a model the long way, like this::

Expand All @@ -72,7 +72,7 @@ Arguments are always passed in as models. You can use quasiquotation (see :hy:fu
(set-to-2 foobar)
(print foobar)

Macros don't understand keyword arguments like functions do. Rather, the :ref:`keyword objects <keywords>` themselves are passed in literally. This gives you flexibility in how to handle them. Thus, ``#** kwargs`` and ``*`` aren't allowed in the parameter list of a macro, although ``#* args`` and ``/`` are.
Macros don't understand keyword arguments like functions do. Rather, the :ref:`keyword objects <keywords>` themselves are passed in literally. This gives you flexibility in how to handle them. Thus, ``#** kwargs`` and ``*`` aren't allowed in the parameter list of a macro, although ``#* args`` and ``/`` are. See :hy:func:`hyrule.defmacro-kwargs` if you want to handle keyword arguments like a function.

On the inside, macros are functions, and obey the usual Python semantics for functions. For example, :hy:func:`setv` inside a macro will define or modify a variable local to the current macro call, and :hy:func:`return` ends macro execution and uses its argument as the expansion.

Expand Down Expand Up @@ -134,7 +134,7 @@ The form ``(import math)`` here appears in the wrong context, in the macro call

(defmacro hypotenuse [a b]
`(hy.I.math.sqrt (+ (** ~a 2) (** ~b 2))))
(hypotenuse 3 4)
(print (hypotenuse 3 4))

A related but distinct issue is when you want to use a function (or other ordinary Python object) in a macro's code, but it isn't available soon enough::

Expand Down Expand Up @@ -195,6 +195,8 @@ Note that because reader macros are evaluated at parse-time, and top-level forms
(print #up "hello?"))
; LexException: reader macro '#up' is not defined

Of the potential problems discussed above that apply to regular macros, such as surprise shadowing, most also apply to reader macros.

.. _macro-namespaces:

Macro namespaces and operations on macros
Expand Down
2 changes: 2 additions & 0 deletions docs/semantics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ variables are, and they aren't explicitly cleaned up, so theoretically, they
can waste memory and lead to :py:meth:`object.__del__` being called later than
you expect. When in doubt, check the ``hy2py`` output.

.. _order-of-eval:

Order of evaluation
-------------------

Expand Down
68 changes: 36 additions & 32 deletions docs/syntax.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ or :py:class:`int`, or you can evaluate a model as Hy code with

Models can be created with the constructors, with the :hy:func:`quote` or
:hy:func:`quasiquote` macros, or with :hy:func:`hy.as-model`. Explicit creation
is often not necessary, because the compiler will autopromote (via
is often not necessary, because the compiler will automatically promote (via
:hy:func:`hy.as-model`) any object it's trying to evaluate.

Note that when you want plain old data structures and don't intend to produce
Expand Down Expand Up @@ -118,24 +118,26 @@ Discard prefix

Like Clojure, Hy supports the Extensible Data Notation discard prefix ``#_``,
which is kind of like a structure-aware comment. When the reader encounters
``#_``, it reads and then discards the following form. Thus ``#_`` is similar
to ``;`` except that reader macros still get executed, and normal parsing
resumes after the next form ends rather than at the start of the next line:
``[dilly #_ and krunk]`` is equivalent to ``[dilly krunk]``, whereas ``[dilly ;
and krunk]`` is equivalent to just ``[dilly``. Comments indicated by ``;`` can
be nested within forms discarded by ``#_``, but ``#_`` has no special meaning
``#_``, it reads and then discards the following form. Thus ``#_`` is like
``;`` except that reader macros still get executed, and normal parsing resumes
after the next form ends rather than at the start of the next line: ``[dilly #_
and krunk]`` is equivalent to ``[dilly krunk]``, whereas ``[dilly ; and
krunk]`` is equivalent to just ``[dilly``. Comments indicated by ``;`` can be
nested within forms discarded by ``#_``, but ``#_`` has no special meaning
within a comment indicated by ``;``.

Identifiers
-----------

Identifiers are a broad class of syntax in Hy, comprising not only variable
names, but any nonempty sequence of characters that aren't ASCII whitespace nor
one of the following: ``()[]{};"'`~``. The reader will attempt to read each
identifier as a :ref:`numeric literal <numeric-literals>`, then attempt to read
it as a :ref:`keyword <keywords>` if that fails, then attempt to read it as a
:ref:`dotted identifier <dotted-identifiers>` if that fails, then fall back on
reading it as a :ref:`symbol <symbols>` if that fails.
one of the following: ``()[]{};"'`~``. The reader will attempt to read an
identifier as each of the following types, in the given order:

1. a :ref:`numeric literal <numeric-literals>`
2. a :ref:`keyword <keywords>`
3. a :ref:`dotted identifier <dotted-identifiers>`
4. a :ref:`symbol <symbols>`

.. _numeric-literals:

Expand Down Expand Up @@ -197,9 +199,9 @@ equivalent to ``{"a" 1 "b" 2}``, which is different from ``{:a 1 :b 2}`` (see
:ref:`dict-literals`).

The empty keyword ``:`` is syntactically legal, but you can't compile a
function call with an empty keyword argument due to Python limitations. Thus
``(foo : 3)`` must be rewritten to use runtime unpacking, as in
``(foo #** {"" 3})``.
function call with an empty keyword argument because of Python limitations.
Thus ``(foo : 3)`` must be rewritten to use runtime unpacking, as in ``(foo #**
{"" 3})``.

.. autoclass:: hy.models.Keyword
:members: __bool__, __call__
Expand Down Expand Up @@ -247,7 +249,7 @@ Lisps). Some example symbols are ``hello``, ``+++``, ``3fiddy``, ``$40``,

Dots are only allowed in a symbol if every character in the symbol is a dot.
Thus, ``a..b`` and ``a.`` are neither dotted identifiers nor symbols; they're
simply illegal syntax.
syntax errors.

As a special case, the symbol ``...`` compiles to the :data:`Ellipsis` object,
as in Python.
Expand Down Expand Up @@ -389,10 +391,10 @@ Expressions

Expressions (:class:`Expression <hy.models.Expression>`) are denoted by
parentheses: ``( … )``. The compiler evaluates expressions by checking the
first element.
first element, called the head.

- If it's a symbol, and the symbol is the name of a currently defined macro,
the macro is called.
- If the head is a symbol, and the symbol is the name of a currently defined
macro, the macro is called.

- Exception: if the symbol is also the name of a function in
:hy:mod:`hy.pyops`, and one of the arguments is an
Expand All @@ -402,18 +404,18 @@ first element.
``(hy.pyops.+ #* summands)``, because Python provides no way to sum a list
of unknown length with a real addition expression.

- If it is itself an expression of the form ``(. None …)`` (typically produced
with a :ref:`dotted identifier <dotted-identifiers>` like ``.add``), it's used
to construct a method call with the element after ``None`` as the object:
thus, ``(.add my-set 5)`` is equivalent to ``((. my-set add) 5)``, which
becomes ``my_set.add(5)`` in Python.
- If the head is itself an expression of the form ``(. None …)`` (typically
produced with a :ref:`dotted identifier <dotted-identifiers>` like ``.add``),
it's used to construct a method call with the element after ``None`` as the
object: thus, ``(.add my-set 5)`` is equivalent to ``((. my-set add) 5)``,
which becomes ``my_set.add(5)`` in Python.

.. _hy.R:

- Exception: expressions like ``((. hy R module-name macro-name) …)``, or equivalently ``(hy.R.module-name.macro-name …)``, get special treatment. They :hy:func:`require` the module ``module-name`` and call its macro ``macro-name``, so ``(hy.R.foo.bar 1)`` is equivalent to ``(require foo) (foo.bar 1)``, but without bringing ``foo`` or ``foo.bar`` into scope. Thus ``hy.R`` is convenient syntactic sugar for macros you'll only call once in a file, or for macros that you want to appear in the expansion of other macros without having to call :hy:func:`require` in the expansion. As with :hy:class:`hy.I`, dots in the module name must be replaced with slashes.

- Otherwise, the expression is compiled into a Python-level call, with the
first element being the calling object. (So, you can call a function that has
head being the calling object. (So, you can call a function that has
the same name as a macro with an expression like ``((do setv) …)``.) The
remaining forms are understood as arguments. Use :hy:func:`unpack-iterable`
or :hy:func:`unpack-mapping` to break up data structures into individual
Expand All @@ -429,10 +431,12 @@ meaning. Trying to compile it is an error. For the empty tuple, use ``#()``.
List, tuple, and set literals
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Literal :class:`list`\s (:class:`List <hy.models.List>`), :class:`tuple`\s
(:class:`Tuple <hy.models.Tuple>`), and :class:`set`\s (:class:`Set
<hy.models.Set>`) are denoted respectively by ``[ … ]``, ``#( … )``, and ``#{ …
}``.
- Literal :class:`list`\s (:class:`List <hy.models.List>`) are denoted by ``[ …
]``.
- Literal :class:`tuple`\s (:class:`Tuple <hy.models.Tuple>`) are denoted by
``#( … )``.
- Literal :class:`set`\s (:class:`Set <hy.models.Set>`) are denoted by ``#{ …
}``.

.. autoclass:: hy.models.List
.. autoclass:: hy.models.Tuple
Expand Down Expand Up @@ -494,9 +498,9 @@ Additional sugar
----------------

Syntactic sugar is available to construct two-item :ref:`expressions
<expressions>` with certain macros. When the sugary characters are encountered
by the reader, a new expression is created with the corresponding macro as the
first element and the next parsed form as the second. No parentheses are
<expressions>` with certain heads. When the sugary characters are encountered
by the reader, a new expression is created with the corresponding macro name as
the first element and the next parsed form as the second. No parentheses are
required. Thus, since ``'`` is short for ``quote``, ``'FORM`` is read as
``(quote FORM)``. Whitespace is allowed, as in ``' FORM``. This is all resolved
at the reader level, so the model that gets produced is the same whether you
Expand Down
Loading

0 comments on commit 225017d

Please sign in to comment.