From 7f783eb6189f90be6104dbb6d066597c28eff87e Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Sat, 2 Jul 2022 10:53:46 -0400 Subject: [PATCH 1/6] Fix some obsolete documentation of `if` --- docs/api.rst | 6 +++--- docs/tutorial.rst | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 55cdf4353..1b828906c 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -111,7 +111,7 @@ base names, such that ``hy.core.macros.foo`` can be called as just ``foo``. ... (dict :name "Dave" :age 5)]) => (defn display-people [people filter] - ... (for [person people] (if (filter person) (print (:name person))))) + ... (for [person people] (when (filter person) (print (:name person))))) => (display-people people (fn [person] (< (:age person) 25))) Alice @@ -714,7 +714,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 @@ -1405,7 +1405,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 diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 19ae4b232..704e4b5f0 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -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 @@ -176,7 +175,7 @@ For branching on more than one case, try :hy:func:`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 +(do THEN-1 THEN-2 …) None)``. ``unless`` works the same as ``when``, but inverts the condition with ``not``. Hy's basic loops are :hy:func:`while` and :hy:func:`for`:: From 3eb0e2171e4c1fb56b5f71a10cf547ac475da5ed Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Sat, 2 Jul 2022 10:54:12 -0400 Subject: [PATCH 2/6] In tutorial, remove early mention of Hyrule macro --- docs/tutorial.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 704e4b5f0..9294abf95 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -175,8 +175,7 @@ For branching on more than one case, try :hy:func:`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 …) None)``. ``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`:: From 69f8c3631022b0ebd1bf48ffb4a1d4245632dad8 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Sat, 2 Jul 2022 10:55:36 -0400 Subject: [PATCH 3/6] Docs typo fix --- docs/syntax.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/syntax.rst b/docs/syntax.rst index 64541d420..ac578abcd 100644 --- a/docs/syntax.rst +++ b/docs/syntax.rst @@ -387,7 +387,7 @@ Expressions Expressions (:class:`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 From 00d5d7b0d2bcebfce7792c1eedc6f74ccf4e0c9d Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Sat, 2 Jul 2022 10:56:21 -0400 Subject: [PATCH 4/6] Clarify literal dictionary syntax --- docs/syntax.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/syntax.rst b/docs/syntax.rst index ac578abcd..6245348e6 100644 --- a/docs/syntax.rst +++ b/docs/syntax.rst @@ -419,11 +419,11 @@ Dictionary literals ~~~~~~~~~~~~~~~~~~~ Literal dictionaries (:class:`dict`, :class:`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 ` 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 ` 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. From da35e0424fae819aa5baabc8112d6a9ae300e68b Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Sat, 2 Jul 2022 10:59:38 -0400 Subject: [PATCH 5/6] Mention the alternative name "Hylang" in Why Hy --- docs/whyhy.rst | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/whyhy.rst b/docs/whyhy.rst index 6d72863c1..c6c00c898 100644 --- a/docs/whyhy.rst +++ b/docs/whyhy.rst @@ -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 `_. -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 +`_. 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 From 1998830f7dbaab216eac976aaf3356dd31073fb8 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Thu, 7 Jul 2022 08:27:45 -0400 Subject: [PATCH 6/6] Rewrite documentation of `defn` and friends --- docs/api.rst | 239 ++++++++++++--------------------------------------- 1 file changed, 55 insertions(+), 184 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 1b828906c..6fd7407f5 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -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] (when (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 + `. .. 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 ` (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...) - 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 ` and an annotation (see :hy:data:`^`) for @@ -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\" \"<\" \">\") - \"\" - - => (format-pair \"A\" :open-text \"<\" :close-text \">\") - \"\" - - #* - 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 \"\", line 1, in - 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 "", 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 "", 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 ` like + Python's ``async def``. .. hy:function:: (defmacro [name lambda-list #* body])