Skip to content

Commit

Permalink
Merge pull request #2314 from Kodiologist/dox
Browse files Browse the repository at this point in the history
Documentation improvements
  • Loading branch information
Kodiologist committed Jul 8, 2022
2 parents e375827 + 1998830 commit d175c5c
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 202 deletions.
243 changes: 57 additions & 186 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -96,64 +96,35 @@ base names, such that ``hy.core.macros.foo`` can be called as just ``foo``.
unknown keys raises an :exc:`IndexError` (on lists and tuples) or a
:exc:`KeyError` (on dictionaries).

.. hy:function:: (fn [name #* arags])
.. hy:function:: (fn [args])
``fn``, like Python's ``lambda``, can be used to define an anonymous function.
Unlike Python's ``lambda``, the body of the function can comprise several
statements. The parameters are similar to ``defn``: the first parameter is
vector of parameters and the rest is the body of the function. ``fn`` returns a
new function. In the following example, an anonymous function is defined and
passed to another function for filtering output::

=> (setv people [(dict :name "Alice" :age 20)
... (dict :name "Bob" :age 25)
... (dict :name "Charlie" :age 50)
... (dict :name "Dave" :age 5)])

=> (defn display-people [people filter]
... (for [person people] (if (filter person) (print (:name person)))))

=> (display-people people (fn [person] (< (:age person) 25)))
Alice
Dave

Just as in normal function definitions, if the first element of the
body is a string, it serves as a docstring. This is useful for giving
class methods docstrings::

=> (setv times-three
... (fn [x]
... "Multiplies input by three and returns the result."
... (* x 3)))

This can be confirmed via Python's built-in ``help`` function::

=> (help times-three)
Help on function times_three:

times_three(x)
Multiplies input by three and returns result
(END)
As :hy:func:`defn`, but no name for the new function is required (or
allowed), and the newly created function object is returned. Decorators
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`. See :hy:func:`fn/a` for the asynchronous equivalent.

.. hy:function:: (fn/a [name #* args])
``fn/a`` is a variant of ``fn`` than defines an anonymous coroutine.
The parameters are similar to ``defn/a``: the first parameter is
vector of parameters and the rest is the body of the function. ``fn/a`` returns a
new coroutine.
As :hy:func:`fn`, but the created function object will be a :ref:`coroutine
<py:async def>`.

.. hy:function:: (defn [name #* args])
Define `name` as a function with `args` as the signature, annotations, and body.

``defn`` is used to define functions. It requires two arguments: a name (given
as a symbol) and a list of parameters (also given as symbols). Any remaining
arguments constitute the body of the function::
``defn`` compiles to a :ref:`function definition <py:function>` (or possibly
to an assignment of a :ref:`lambda expression <py:lambda>`). 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...)

If there at least two body forms, and the first of them is a string literal,
this string becomes the :term:`py:docstring` of the function.
An empty body is implicitly ``(return None)``. If there 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))``.

``defn`` accepts two additional, optional arguments: a bracketed list of
:term:`decorators <py:decorator>` and an annotation (see :hy:data:`^`) for
Expand All @@ -162,152 +133,52 @@ base names, such that ``hy.core.macros.foo`` can be called as just ``foo``.

(defn [decorator1 decorator2] ^annotation name [params] …)

Parameters may be prefixed with the following special symbols. If you use more
than one, they can only appear in the given order (so all positional only arguments
must precede ``/``, all positional or keyword arguments must precede a ``#*`` rest
parameter or ``*`` kwonly delimiter and ``#**`` must be the last argument).
This is the same order that Python requires.

/
The preceding parameters can only be supplied as positional arguments.

positional or keyword arguments:
All parameters until following ``/`` (if its supplied) but before ``*/#*/#**``
can be supplied positionally or by keyword. Optional arguments may be given as
two-argument lists, where the first element is the parameter name and the second
is the default value. When defining parameters, a positional argument cannot follow
a keyword argument.

The following example defines a function with one required positional argument
as well as three optional arguments. The first optional argument defaults to ``None``
and the latter two default to ``\"(\"`` and ``\")\"``, respectively::

=> (defn format-pair [left-val [right-val None] [open-text \"(\"] [close-text \")\"]]
... (+ open-text (str left-val) \", \" (str right-val) close-text))

=> (format-pair 3)
\"(3, None)\"

=> (format-pair \"A\" \"B\")
\"(A, B)\"

=> (format-pair \"A\" \"B\" \"<\" \">\")
\"<A, B>\"

=> (format-pair \"A\" :open-text \"<\" :close-text \">\")
\"<A, None>\"

#*
The following parameter will contain a list of 0 or more positional arguments.
No other positional parameters may be specified after this one. Parameters
defined after this but before ``#**`` are considered keyword only.

The following code example defines a function that can be given 0 to n
numerical parameters. It then sums every odd number and subtracts
every even number::

=> (defn zig-zag-sum [#* numbers]
(setv odd-numbers (lfor x numbers :if (% x 2) x)
even-numbers (lfor x numbers :if (= (% x 2) 0) x))
(- (sum odd-numbers) (sum even-numbers)))

=> (zig-zag-sum)
0
=> (zig-zag-sum 3 9 4)
8
=> (zig-zag-sum 1 2 3 4 5 6)
-3

*

All following parmaeters can only be supplied as keywords.
Like keyword arguments, the parameter may be marked as optional by
declaring it as a two-element list containing the parameter name
following by the default value. Parameters without a default are
considered required::

=> (defn compare [a b * keyfn [reverse False]]
... (setv result (keyfn a b))
... (if (not reverse)
... result
... (- result)))
=> (compare \"lisp\" \"python\"
... :keyfn (fn [x y]
... (reduce - (map (fn [s] (ord (get s 0))) [x y]))))
-4
=> (compare \"lisp\" \"python\"
... :keyfn (fn [x y]
... (reduce - (map (fn [s] (ord (get s 0))) [x y])))
... :reverse True)
4

.. code-block:: python
=> (compare \"lisp\" \"python\")
Traceback (most recent call last):
File \"<input>\", line 1, in <module>
TypeError: compare() missing 1 required keyword-only argument: 'keyfn'
#**
Like ``#*``, but for keyword arguments.
The following parameter will contain 0 or more keyword arguments.
The following code examples defines a function that will print all keyword
arguments and their values::
=> (defn print-parameters [#** kwargs]
... (for [#(k v) (.items kwargs)] (print k v)))
=> (print-parameters :parameter-1 1 :parameter-2 2)
parameter_1 1
parameter_2 2
; to avoid the mangling of '-' to '_', use unpacking:
=> (print-parameters #** {\"parameter-1\" 1 \"parameter-2\" 2})
parameter-1 1
parameter-2 2
.. _reserved_param_names:
.. note::
To define asynchronous functions, see :hy:func:`defn/a` and :hy:func:`fn/a`.

Parameter names cannot be Python reserved words nor can a function
be called with keyword arguments that are Python reserved words. This means
that the following will raise a `SyntaxError` as they would in Python::
``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.

(defn afunc [a b if])
Traceback (most recent call last):
File "<stdin>", line 1
(defn afunc [a b if])
^
hy.errors.HySyntaxError: parameter name cannot be Python reserved word
Further special lambda-list syntax includes:

(dict :a 1 :from 2)
Traceback (most recent call last):
File "<stdin>", line 1
(dict :a 1 :from 2)
^
hy.errors.HySyntaxError: keyword argument cannot be Python reserved word
``/``
If the symbol ``/`` is given in place of a parameter, it means that all
the preceding parameters can only be set positionally.

This only applies to parameter names and a keyword argument name. The value of
the parameter or keyword argument can still be a keyword of a reserved word::
``*``
If the symbol ``*`` is given in place of a parameter, it means that all
the following parameters can only be set by name.

=> (defn test [a] a)
=> (test :a :from)
:from
``#* 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.

.. hy:function:: (defn/a [name lambda-list #* body])
``#** kwargs``
``#** kwargs`` (a.k.a. ``(unpack-mapping kwargs)``) is like ``#*
args``, but collects unmatched keyword arguments into a dictionary.

Define `name` as a function with `lambda-list` signature and body `body`.
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/a`` macro is a variant of ``defn`` that instead defines
coroutines. It takes three parameters: the *name* of the function to
define, a vector of *parameters*, and the *body* of the function:
(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}]

Examples:
::
.. hy:function:: (defn/a [name lambda-list #* body])
=> (defn/a name [params] body)
As :hy:func:`defn`, but defines a :ref:`coroutine <py:async def>` like
Python's ``async def``.

.. hy:function:: (defmacro [name lambda-list #* body])
Expand Down Expand Up @@ -714,7 +585,7 @@ base names, such that ``hy.core.macros.foo`` can be called as just ``foo``.
iteration clause before the ``:do``.
- ``:setv LVALUE RVALUE``, which is equivalent to ``:do (setv LVALUE
RVALUE)``.
- ``:if CONDITION``, which is equivalent to ``:do (if (not CONDITION)
- ``:if CONDITION``, which is equivalent to ``:do (when (not CONDITION)
(continue))``.

For ``lfor``, ``sfor``, ``gfor``, and ``dfor``, variables defined by
Expand Down Expand Up @@ -1405,7 +1276,7 @@ base names, such that ``hy.core.macros.foo`` can be called as just ``foo``.
'(+ 1 2 3 4)
=> `(+ ~@nums)
'(+ 1 2 3 4)
=> `[1 2 ~@(if (< (get nums 0) 0) nums)]
=> `[1 2 ~@(when (< (get nums 0) 0) nums)]
'[1 2]

Here, the last example evaluates to ``('+' 1 2)``, since the condition
Expand Down
12 changes: 6 additions & 6 deletions docs/syntax.rst
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ Expressions

Expressions (:class:`Expression <hy.models.Expression>`) are denoted by
parentheses: ``( … )``. The compiler evaluates expressions by checking the
first element. If it's a symbol, and the symbol has the name of a currently
first element. If it's a symbol, and the symbol is the name of a currently
defined macro, the macro is called. Otherwise, the expression is compiled into
a Python-level call, with the first element being the calling object. The
remaining forms are understood as arguments. Use :hy:func:`unpack-iterable` or
Expand Down Expand Up @@ -419,11 +419,11 @@ Dictionary literals
~~~~~~~~~~~~~~~~~~~

Literal dictionaries (:class:`dict`, :class:`Dict <hy.models.Dict>`) are
denoted by ``{ … }``. Odd-numbered child forms become the keys whereas
even-numbered child forms become the values. For example, ``{"a" 1 "b" 2}``
produces a dictionary mapping ``"a"`` to ``1`` and ``"b"`` to ``2``. Trying to
compile a :class:`Dict <hy.models.Dict>` with an odd number of child models is
an error.
denoted by ``{ … }``. Even-numbered child forms (counting the first as 0)
become the keys whereas odd-numbered child forms become the values. For
example, ``{"a" 1 "b" 2}`` produces a dictionary mapping ``"a"`` to ``1`` and
``"b"`` to ``2``. Trying to compile a :class:`Dict <hy.models.Dict>` with an
odd number of child models is an error.

As in Python, calling :class:`dict` with keyword arguments is often more
convenient than using a literal dictionary.
Expand Down
6 changes: 2 additions & 4 deletions docs/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,7 @@ Conditional logic can be built with :hy:func:`if`::

As in this example, ``if`` is called like ``(if CONDITION THEN ELSE)``. It
executes and returns the form ``THEN`` if ``CONDITION`` is true (according to
:class:`bool`) and ``ELSE`` otherwise. If ``ELSE`` is omitted, ``None`` is used
in its place.
:class:`bool`) and ``ELSE`` otherwise.

What if you want to use more than form in place of the ``THEN`` or ``ELSE``
clauses, or in place of ``CONDITION``, for that matter? Use the macro
Expand All @@ -176,8 +175,7 @@ For branching on more than one case, try :hy:func:`cond <hy.core.macros.cond>`::
(print "That variable is jussssst right!"))

The macro ``(when CONDITION THEN-1 THEN-2 …)`` is shorthand for ``(if CONDITION
(do THEN-1 THEN-2 …))``. ``unless`` works the same as ``when``, but inverts the
condition with ``not``.
(do THEN-1 THEN-2 …) None)``.

Hy's basic loops are :hy:func:`while` and :hy:func:`for`::

Expand Down
13 changes: 7 additions & 6 deletions docs/whyhy.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
Why Hy?
=======

Hy (named after the insect order Hymenoptera, since Paul Tagliamonte
was studying swarm behavior when he created the language) is a
multi-paradigm general-purpose programming language in the `Lisp
family <https://en.wikipedia.org/wiki/Lisp_(programming_language)>`_.
It's implemented as a kind of alternative syntax for Python. Compared
to Python, Hy offers a variety of extra features, generalizations, and
Hy (or "Hylang" for long; named after the insect order Hymenoptera,
since Paul Tagliamonte was studying swarm behavior when he created the
language) is a multi-paradigm general-purpose programming language in
the `Lisp family
<https://en.wikipedia.org/wiki/Lisp_(programming_language)>`_. It's
implemented as a kind of alternative syntax for Python. Compared to
Python, Hy offers a variety of extra features, generalizations, and
syntactic simplifications, as would be expected of a Lisp. Compared to
other Lisps, Hy provides direct access to Python's built-ins and
third-party Python libraries, while allowing you to freely mix
Expand Down

0 comments on commit d175c5c

Please sign in to comment.