From a8d431146ab3d748dec1785f06e0562870a13f09 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Mon, 2 Oct 2023 14:49:46 -0400 Subject: [PATCH] Lots of small improvements to the manual --- docs/cli.rst | 8 ++++---- docs/index.rst | 3 ++- docs/interop.rst | 15 +++++---------- docs/repl.rst | 2 +- docs/semantics.rst | 4 ++-- docs/syntax.rst | 38 ++++++++++++++++---------------------- docs/tutorial.rst | 22 ++++++++++++++-------- docs/whyhy.rst | 41 +++++++++++++++++++++++++---------------- hy/models.py | 2 +- 9 files changed, 70 insertions(+), 65 deletions(-) diff --git a/docs/cli.rst b/docs/cli.rst index d552c59ba..10bc1756b 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -35,7 +35,7 @@ for a complete list of options and :py:ref:`Python's documentation .. cmdoption:: --repl-output-fn Set the :ref:`REPL output function `. This can be the - name of a Python builtin, mostly likely ``repr``, or a dotted name like + name of a Python builtin, most likely ``repr``, or a dotted name like ``foo.bar.baz``. In the latter case, Hy will attempt to import the named object with code like ``(import foo.bar [baz])``. @@ -45,10 +45,10 @@ for a complete list of options and :py:ref:`Python's documentation hy2py ----- -``hy2py`` is a program to convert Hy source code into Python source code. Use ``hy2py --help`` for usage instructions. It can take its input from standard input, or from a file or module name provided as a command-line argument. In the case of a module name, the current working directory should be the parent directory of that module, and the output parameter (``--output/-o``) is required. When the output parameter is provided, the output will be written into the folder or file. Otherwise, the result is written to standard output. +``hy2py`` is a program to convert Hy source code into Python source code. Use ``hy2py --help`` for usage instructions. It can take its input from standard input, or from a file or module name provided as a command-line argument. In the case of a module name, the current working directory should be the parent directory of that module, and the output parameter (``--output/-o``) is required. When the output parameter is provided, the output will be written into the given folder or file. Otherwise, the result is written to standard output. .. warning:: - ``hy2py`` can execute arbitrary code. Don't give it untrusted input. + ``hy2py`` can execute arbitrary code (via macros, :hy:func:`eval-when-compile`, etc.). Don't give it untrusted input. @@ -60,4 +60,4 @@ hyc ``hyc`` is a program to compile files of Hy code into Python bytecode. Use ``hyc --help`` for usage instructions. The generated bytecode files are named and placed according to the usual scheme of your Python executable, as indicated by :py:func:`importlib.util.cache_from_source`. .. warning:: - ``hyc`` can execute arbitrary code. Don't give it untrusted input. + ``hyc`` can execute arbitrary code (via macros, :hy:func:`eval-when-compile`, etc.). Don't give it untrusted input. diff --git a/docs/index.rst b/docs/index.rst index c33294927..b6b855378 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -14,7 +14,8 @@ Hy is a Lisp dialect that's embedded in Python. Since Hy transforms its Lisp code into Python abstract syntax tree (AST) objects, you have the whole beautiful world of Python at your fingertips, in Lisp form. -.. Changes to this paragraph should be mirrored on Hy's homepage. +.. Changes to the below paragraph should be mirrored on Hy's homepage + and the README. To install the latest release of Hy, just use the command ``pip3 install --user hy``. Then you can start an interactive read-eval-print loop (REPL) with diff --git a/docs/interop.rst b/docs/interop.rst index cf0d8a4a4..8c3b813f7 100644 --- a/docs/interop.rst +++ b/docs/interop.rst @@ -4,20 +4,17 @@ Python Interoperability ======================= -Despite being a Lisp, Hy aims to be fully compatible with Python. That means -every Python module or package can be imported in Hy code, and vice versa. - :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 -``is_valid`` in Python. You can call :hy:func:`hy.mangle` and +``hyx_valid_Xquestion_markX`` in Python. You can call :hy:func:`hy.mangle` and :hy:func:`hy.unmangle` from either language. Using Python from Hy ==================== -To use a Python module from Hy, just :hy:func:`import` it. No additional -ceremony is required. +To use a Python module from Hy, just :hy:func:`import` it. In most cases, no +additional ceremony is required. You can embed Python code directly into a Hy program with the macros :hy:func:`py ` and :hy:func:`pys `, and you can use standard Python @@ -43,7 +40,7 @@ still import ``hy``, and thus require Hy to be installed in order to run; see :ref:`implicit-names` for details and workarounds. To execute Hy code from a string, use :hy:func:`hy.read-many` to convert it to -:ref:`models ` and then :hy:func:`hy.eval` to evaluate it: +:ref:`models ` and :hy:func:`hy.eval` to evaluate it: .. code-block:: python @@ -59,9 +56,7 @@ You can use :meth:`hy.REPL.run` to launch the Hy REPL from Python, as in 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, +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 diff --git a/docs/repl.rst b/docs/repl.rst index 838f169fd..67af1540b 100644 --- a/docs/repl.rst +++ b/docs/repl.rst @@ -26,7 +26,7 @@ Output functions By default, the return value of each REPL input is printed with :hy:func:`hy.repr`. To change this, you can set the REPL output function with e.g. the command-line argument ``--repl-output-fn``. Use :func:`repr` to get -Python representations like Python's own REPL. +Python representations, like Python's own REPL. Regardless of the output function, no output is produced when the value is ``None``, as in Python. diff --git a/docs/semantics.rst b/docs/semantics.rst index f47cca68b..626c091d9 100644 --- a/docs/semantics.rst +++ b/docs/semantics.rst @@ -49,10 +49,10 @@ called first, call it before ``f``. When bytecode is regenerated ---------------------------- -The first time Hy is asked to execute a file, it will produce a bytecode file +The first time Hy is asked to execute a file, whether directly or indirectly (as in the case of an import), it will produce a bytecode file (unless :std:envvar:`PYTHONDONTWRITEBYTECODE` is set). Subsequently, if the source file hasn't changed, Hy will load the bytecode instead of recompiling -the source. Python behaves similarly, but the difference between recompilation +the source. Python also makes bytecode files, but the difference between recompilation and loading bytecode is more consequential in Hy because of how Hy lets you run and generate code at compile-time with things like macros, reader macros, and :hy:func:`eval-and-compile`. You may be surprised by behavior like the diff --git a/docs/syntax.rst b/docs/syntax.rst index b7f84436a..1a55eb7a2 100644 --- a/docs/syntax.rst +++ b/docs/syntax.rst @@ -26,12 +26,12 @@ somewhat different (such as :class:`Set `, which is ordered, unlike actual :class:`set`\s). All models inherit from :class:`Object `, which stores textual position information, so tracebacks can point to the right place in the code. The compiler takes whatever models -are left over after parsing and macro expansion and translates them into Python +are left over after parsing and macro-expansion and translates them into Python :mod:`ast` nodes (e.g., :class:`Integer ` becomes :class:`ast.Constant`), which can then be evaluated or rendered as Python code. Macros (that is, regular macros, as opposed to reader macros) operate on the model level, taking some models as arguments and returning more models for -compilation or further macro expansion; they're free to do quite different +compilation or further macro-expansion; they're free to do quite different things with a given model than the compiler does, if it pleases them to, like using an :class:`Integer ` to construct a :class:`Symbol `. @@ -177,8 +177,8 @@ Literal keywords are most often used for their special treatment in as values. For example, ``(f :foo 3)`` calls the function ``f`` with the parameter ``foo`` set to ``3``. The keyword is also :ref:`mangled ` at compile-time. To prevent a literal keyword from being treated specially in -an expression, you can :hy:func:`quote` the keyword, or you can use it itself -as a keyword argument, as in ``(f :foo :bar)``. +an expression, you can :hy:func:`quote` the keyword, or you can use it as the +value for another keyword argument, as in ``(f :foo :bar)``. Otherwise, keywords are simple model objects that evaluate to themselves. Users of other Lisps should note that it's often a better idea to use a string than a @@ -312,7 +312,7 @@ Hy allows double-quoted strings (e.g., ``"hello"``), but not single-quoted strings like Python. The single-quote character ``'`` is reserved for preventing the evaluation of a form, (e.g., ``'(+ 1 1)``), as in most Lisps (see :ref:`more-sugar`). Python's so-called triple-quoted strings (e.g., -``'''hello'''`` and ``"""hello"""``) aren't supported. However, in Hy, unlike +``'''hello'''`` and ``"""hello"""``) aren't supported, either. However, in Hy, unlike Python, any string literal can contain newlines; furthermore, Hy has :ref:`bracket strings `. For consistency with Python's triple-quoted strings, all literal newlines in literal strings are read as in @@ -324,8 +324,8 @@ Unrecognized escape sequences are a syntax error. To create a "raw string" that interprets all backslashes literally, prefix the string with ``r``, as in ``r"slash\not"``. -Like Python, Hy treats all string literals as sequences of Unicode characters -by default. The result is the model type :class:`String `. +By default, all string literals are regarded as sequences of Unicode characters. +The result is the model type :class:`String `. You may prefix a string literal with ``b`` to treat it as a sequence of bytes, producing :class:`Bytes ` instead. @@ -351,10 +351,10 @@ like the here-documents of other languages. A bracket string begins with begins with ``f-``, the bracket string is interpreted as an :ref:`f-string `.) For example:: - => (print #[["That's very kind of yuo [sic]" Tom wrote back.]]) - "That's very kind of yuo [sic]" Tom wrote back. - => (print #[==[1 + 1 = 2]==]) - 1 + 1 = 2 + (print #[["That's very kind of yuo [sic]" Tom wrote back.]]) + ; "That's very kind of yuo [sic]" Tom wrote back. + (print #[==[1 + 1 = 2]==]) + ; 1 + 1 = 2 Bracket strings are always raw Unicode strings, and don't allow the ``r`` or ``b`` prefixes. @@ -451,12 +451,9 @@ A format string (or "f-string", or "formatted string literal") is a string literal with embedded code, possibly accompanied by formatting commands. The result is an :class:`FString `, Hy f-strings work much like :ref:`Python f-strings ` except that the embedded code is in Hy -rather than Python. +rather than Python. :: -:: - - => (print f"The sum is {(+ 1 1)}.") - The sum is 2. + (print f"The sum is {(+ 1 1)}.") ; => The sum is 2. Since ``=``, ``!``, and ``:`` are identifier characters in Hy, Hy decides where the code in a replacement field ends (and any debugging ``=``, conversion @@ -464,12 +461,9 @@ specifier, or format specifier begins) by parsing exactly one form. You can use ``do`` to combine several forms into one, as usual. Whitespace may be necessary to terminate the form:: - => (setv foo "a") - => (print f"{foo:x<5}") - … - NameError: name 'hyx_fooXcolonXxXlessHthan_signX5' is not defined - => (print f"{foo :x<5}") - axxxx + (setv foo "a") + (print f"{foo:x<5}") ; => NameError: name 'hyx_fooXcolonXxXlessHthan_signX5' is not defined + (print f"{foo :x<5}") ; => axxxx Unlike Python, whitespace is allowed between a conversion and a format specifier. diff --git a/docs/tutorial.rst b/docs/tutorial.rst index b20e69b98..929304731 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -65,11 +65,13 @@ the **form**. ``92``, ``*``, and ``(* 92 2)`` are all forms. A Lisp program consists of a sequence of forms nested within forms. Forms are typically separated from each other by whitespace, but some forms, such as string literals (``"Hy, world!"``), can contain whitespace themselves. An -**expression** is a form enclosed in parentheses; its first child form, called -the **head**, determines what the expression does, and should generally be a -function or macro. Functions are the most ordinary sort of head, whereas macros -(described in more detail below) are functions executed at compile-time instead -and return code to be executed at run-time. +:ref:`expression ` is a form enclosed in parentheses; its first +child form, called the **head**, determines what the expression does, and +should generally be a function or macro. :py:term:`Functions `, the +most ordinary sort of head, constitute reusable pieces of code that can take in +arguments and return a value. Macros (described in more detail :ref:`below +`) are a special kind of function that's executed at +compile-time and returns code to be executed at run-time. Comments start with a ``;`` character and continue till the end of the line. A comment is functionally equivalent to whitespace. :: @@ -260,6 +262,8 @@ Python can import a Hy module like any other module so long as Hy itself has been imported first, which, of course, must have already happened if you're running a Hy program. +.. _tutorial-macros: + Macros ====== @@ -277,7 +281,9 @@ program. Here's a simple example:: (print "Value:" (m)) (print "Done executing") -If you run this program twice in a row, you'll see this:: +If you run this program twice in a row, you'll see this: + +.. code-block:: text $ hy example.hy Now for a slow computation @@ -300,8 +306,8 @@ simply:: (print "Value:" 1) (print "Done executing") -Our macro ``m`` has an especially simple return value, an integer, which at -compile-time is converted to an integer literal. In general, macros can return +Our macro ``m`` has an especially simple return value, an integer (:py:class:`int`), which at +compile-time is converted to an integer model (:class:`hy.models.Integer`). In general, macros can return arbitrary Hy forms to be executed as code. There are several helper macros that make it easy to construct forms programmatically, such as :hy:func:`quote` (``'``), :hy:func:`quasiquote` (`````), :hy:func:`unquote` (``~``), diff --git a/docs/whyhy.rst b/docs/whyhy.rst index b4662ece9..e2c8f50da 100644 --- a/docs/whyhy.rst +++ b/docs/whyhy.rst @@ -2,7 +2,7 @@ Why Hy? ======= -.. Changes to this paragraph should be mirrored on Hy's homepage. +.. Changes to the below paragraph should be mirrored on Hy's homepage. Hy (or "Hylang" for long; named after the insect order Hymenoptera, since Paul Tagliamonte was studying swarm behavior when he created the @@ -22,19 +22,27 @@ Hy versus Python The first thing a Python programmer will notice about Hy is that it has Lisp's traditional parenthesis-heavy prefix syntax in place of Python's C-like infix -syntax. For example, ``print("The answer is", 2 + object.method(arg))`` could -be written ``(print "The answer is" (+ 2 (.method object arg)))`` in Hy. -Consequently, Hy is free-form: structure is indicated by punctuation rather +syntax. For example, + +.. code-block:: python + + print("The answer is", 2 + object.method(arg)) + +could be written :: + + (print "The answer is" (+ 2 (.method object arg))) + +in Hy. Consequently, Hy is free-form: structure is indicated by punctuation rather than whitespace, making it convenient for command-line use. As in other Lisps, the value of a simplistic syntax is that it facilitates Lisp's signature feature: `metaprogramming -`_ through macros, which are -functions that manipulate code objects at compile time to produce new code -objects, which are then executed as if they had been part of the original code. -In fact, Hy allows arbitrary computation at compile-time. For example, here's a -simple macro that implements a C-style do-while loop, which executes its body -for as long as the condition is true, but at least once. +`_ through :doc:`macros +`, which are functions that manipulate code objects at compile time to +produce new code objects, which are then executed as if they had been part of +the original code. In fact, Hy allows arbitrary computation at compile-time. For +example, here's a simple macro that implements a C-style do-while loop, which +executes its body for as long as the condition is true, but at least once. .. _do-while: @@ -52,7 +60,7 @@ for as long as the condition is true, but at least once. Hy also removes Python's restrictions on mixing expressions and statements, allowing for more direct and functional code. For example, Python doesn't allow -:ref:`with ` blocks, which close a resource once you're done using it, +:keyword:`with` blocks, which close a resource once you're done using it, to return values. They can only execute a set of statements: .. code-block:: python @@ -70,7 +78,7 @@ it like an ordinary function call:: (len (with [o (open "foo")] (.read o))) (len (with [o (open "bar")] (.read o))))) -To be even more concise, you can put a ``with`` form in a :hy:func:`gfor `:: +To be even more concise, you can put a ``with`` form in a :hy:func:`gfor`:: (print (sum (gfor filename ["foo" "bar"] @@ -81,9 +89,9 @@ Operators can be given more than two arguments (e.g., ``(+ 1 2 3)``), including augmented assignment operators (e.g., ``(+= x 1 2 3)``). They are also provided as ordinary first-class functions of the same name, allowing them to be passed to higher-order functions: ``(sum xs)`` could be written ``(reduce + xs)``, -after importing the function ``+`` from the module ``hy.pyops``. +after importing the function ``+`` from the module :hy:mod:`hy.pyops`. -The Hy compiler works by reading Hy source code into Hy model objects and +The Hy compiler works by reading Hy source code into Hy :ref:`model objects ` and compiling the Hy model objects into Python abstract syntax tree (:py:mod:`ast`) objects. Python AST objects can then be compiled and run by Python itself, byte-compiled for faster execution later, or rendered into Python source code. @@ -131,8 +139,9 @@ in a set literal. However, models can be concatenated and indexed just like plain lists, and you can return ordinary Python types from a macro or give them to :hy:func:`hy.eval` and Hy will automatically promote them to models. -Hy takes much of its semantics from Python. For example, Hy is a Lisp-1 because -Python functions use the same namespace as objects that aren't functions. In +Hy takes much of its semantics from Python. For example, functions use the same +namespace as objects that aren't functions, so a variable named ``globals`` +can shadow the Python built-in function :py:func:`globals`. In general, any Python code should be possible to literally translate to Hy. At the same time, Hy goes to some lengths to allow you to do typical Lisp things that aren't straightforward in Python. For example, Hy provides the diff --git a/hy/models.py b/hy/models.py index 63f8a529c..3ce3a74ed 100644 --- a/hy/models.py +++ b/hy/models.py @@ -537,7 +537,7 @@ class Dict(Sequence): Represents a literal :class:`dict`. ``keys``, ``values``, and ``items`` methods are provided, each returning a list, although this model type does none of the normalization of a real :class:`dict`. In the case of an odd number of child models, - ``keys`` returns the last child whereas ``values`` and ``items`` ignores it. + ``keys`` returns the last child whereas ``values`` and ``items`` ignore it. """ def _pretty_str(self):