Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Big ol' documentation rewrite #94

Merged
merged 11 commits into from
Apr 23, 2024
4 changes: 4 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ Reference
----------------------------------------------------------------------
.. hy:automodule:: hyrule.destructure

API
~~~~~~~~~~

.. hy:automacro:: defn+
.. hy:automacro:: defn/a+
.. hy:automacro:: dict=:
Expand Down Expand Up @@ -134,6 +137,7 @@ Reference
.. hy:autofunction:: constantly
.. hy:autofunction:: dec
.. hy:autofunction:: inc
.. hy:automacro:: of
.. hy:autofunction:: parse-args
.. hy:automacro:: profile/calls
.. hy:automacro:: profile/cpu
Expand Down
207 changes: 64 additions & 143 deletions hyrule/anaphoric.hy
Original file line number Diff line number Diff line change
@@ -1,57 +1,38 @@
;;; Hy anaphoric macros
"The anaphoric macros module makes functional programming in Hy very
concise and easy to read.
"Hyrule's anaphoric macros can make functional programming more concise and
easier to read. An anaphoric macro assigns values to designated symbols
(typically ``it``) that may be used in the code passed to the macro."

An anaphoric macro is a type of programming macro that
deliberately captures some form supplied to the macro which may be
referred to by an anaphor (an expression referring to another).

-- Wikipedia (https://en.wikipedia.org/wiki/Anaphoric_macro)"

(require
hyrule.macrotools [defmacro!]
hyrule.argmove [->])


(defmacro ap-if [test-form then-form [else-form None]]
"As :ref:`if <if>`, but the result of the test form is named ``it`` in
the subsequent forms. The else-clause is optional.

Examples:
::
(defmacro ap-if [test true-value [false-value None]]
#[[As :hy:func:`if`, but the result of the test form is named ``it`` in
the subsequent forms, and the else-clause is optional. ::

=> (import os)
=> (ap-if (.get os.environ \"PYTHONPATH\")
... (print \"Your PYTHONPATH is\" it))
"
`(let [it ~test-form]
(if it ~then-form ~else-form)))
(import os)
(ap-if (.get os.environ "PYTHONPATH")
(print "Your PYTHONPATH is" it))]]
`(let [it ~test]
(if it ~true-value ~false-value)))


(defmacro ap-each [xs #* body]
"Evaluate the body forms for each element ``it`` of ``xs`` and return ``None``.
"Evaluate the body forms for each element ``it`` of ``xs`` and return
``None``. ::

Examples:
::

=> (ap-each [1 2 3] (print it))
1
2
3"
(ap-each [1 2 3] (print it))"
`(let [it None] (for [it ~xs] ~@body)))


(defmacro ap-each-while [xs form #* body]
"As ``ap-each``, but the form ``pred`` is run before the body forms on
each iteration, and the loop ends if ``pred`` is false.

Examples:
::
"As :hy:func:`ap-each`, but the form ``pred`` is run before the body forms on
each iteration, and the loop ends if ``pred`` is false. ::

=> (ap-each-while [1 2 3 4 5 6] (< it 4) (print it))
1
2
3"
(ap-each-while [1 2 3 4 5 6] (< it 4) (print it))
; Prints only 1, 2, and 3"
`(let [it None]
(for [it ~xs]
(when (not ~form)
Expand All @@ -61,67 +42,42 @@ concise and easy to read.

(defmacro ap-map [form xs]
"Create a generator like :py:func:`map` that yields each result of ``form``
evaluated with ``it`` bound to successive elements of ``xs``.

Examples:
::
evaluated with ``it`` bound to successive elements of ``xs``. ::

=> (list (ap-map (* it 2) [1 2 3]))
[2 4 6]"
(list (ap-map (* it 2) [1 2 3])) ; => [2 4 6]"
`(gfor it ~xs ~form))


(defmacro ap-map-when [predfn rep xs]
"As ``ap-map``, but the predicate function ``predfn`` (yes, that's a
function, not an anaphoric form) is applied to each ``it``, and the
anaphoric mapping form ``rep`` is only applied if the predicate is true.
Otherwise, ``it`` is yielded unchanged.

Examples:
::

=> (list (ap-map-when (fn [x] (% x 2)) (* it 2) [1 2 3 4]))
[2 2 6 4]

::

=> (list (ap-map-when (fn [x] (= (% x 2) 0)) (* it 2) [1 2 3 4]))
[1 4 3 8]"
"As :hy:func:`ap-map`, but the predicate function ``predfn`` (yes, that's a
function, not an anaphoric form) is applied to each ``it``, and the anaphoric
mapping form ``rep`` is only applied if the predicate is true. Otherwise,
``it`` is yielded unchanged. ::

(list (ap-map-when (fn [x] (% x 2)) (* it 2) [1 2 3 4]))
; => [2 2 6 4]
(list (ap-map-when (fn [x] (= (% x 2) 0)) (* it 2) [1 2 3 4]))
; => [1 4 3 8]"
`(gfor it ~xs (if (~predfn it) ~rep it)))


(defmacro ap-filter [form xs]
"The :py:func:`filter` equivalent of ``ap-map``.
"The :py:func:`filter` equivalent of :hy:func:`ap-map`.

Examples:
::
::

=> (list (ap-filter (> (* it 2) 6) [1 2 3 4 5]))
[4 5]"
(list (ap-filter (> (* it 2) 6) [1 2 3 4 5]))
; => [4 5]"
`(gfor it ~xs :if ~form it))


(defmacro ap-reject [form xs]
"Equivalent to ``(ap-filter (not form) xs)``.

Examples:
::

=> (list (ap-reject (> (* it 2) 6) [1 2 3 4 5]))
[1 2 3]"
"Shorthand for ``(ap-filter (not form) xs)``. See :hy:func:`ap-filter`."
`(gfor it ~xs :if (not ~form) it))


(defmacro ap-dotimes [n #* body]
"Equivalent to ``(ap-each (range n) body…)``.

Examples:
::

=> (setv n [])
=> (ap-dotimes 3 (.append n it))
=> n
[0 1 2]"
"Shorthand for ``(ap-each (range n) body…)``. See :hy:func:`ap-each`."
`(let [it None]
(for [it (range ~n)]
~@body)))
Expand All @@ -130,30 +86,20 @@ concise and easy to read.
(defmacro ap-first [form xs]
"Evaluate the predicate ``form`` for each element ``it`` of ``xs``. When
the predicate is true, stop and return ``it``. If the predicate is never
true, return ``None``.

Examples:
::
true, return ``None``. ::

=> (ap-first (> it 5) (range 10))
6"
(ap-first (> it 5) (range 10)) ; => 6"
`(next
(gfor it ~xs :if ~form it)
None))


(defmacro ap-last [form xs]
"Usage: ``(ap-last form list)``

Evaluate the predicate ``form`` for every element ``it`` of ``xs``.
"Evaluate the predicate ``form`` for every element ``it`` of ``xs``.
Return the last element for which the predicate is true, or ``None`` if
there is no such element.

Examples:
::
there is no such element. ::

=> (ap-last (> it 5) (range 10))
9"
(ap-last (> it 5) (range 10)) ; => 9"
(setv x (hy.gensym))
`(let [it None]
(setv ~x None)
Expand All @@ -174,13 +120,9 @@ concise and easy to read.

If ``initial-value`` is supplied, the process instead begins with
``acc`` set to ``initial-value`` and ``it`` set to the first element of
``xs``.

Examples:
::
``xs``. ::

=> (ap-reduce (+ it acc) (range 10))
45"
(ap-reduce (+ it acc) (range 10)) ; => 45"
`(let [acc None it None]
(setv acc ~(if (is initial-value None)
`(do
Expand All @@ -192,63 +134,42 @@ concise and easy to read.
acc))

(defmacro ap-when [test-form #* body]
"As :hy:func:`when <hy.core.macros.when>`, but the result of the test
form is named ``it`` in the subsequent forms.

Examples:
::

=> (import os)
=> (ap-when (.get os.environ \"PYTHONPATH\")
... (print \"Your PYTHONPATH is\" it)
... it)
"
#[[As :hy:func:`when <hy.core.macros.when>`, but the result of the test
form is named ``it`` in the subsequent forms. ::

(setv variable -1)
(ap-when (+ variable 2)
(setv result it)
(print it))
(print result)]]
`(let [it ~test-form]
(when it ~@body)))

(defmacro ap-with [form #* body]
"As :ref:`with <with>`, but the result of the form is named ``it`` in
the subsequent forms.

Examples:
::

=> (ap-with (open \"/proc/cpuinfo\")
... (lfor line it line))
"
"Shorthand for ``(with [it form] body…)``. See :hy:func:`with`."
`(with [it ~form]
~@body))

(defreader %
"Makes an expression into a function with an implicit ``%`` parameter list.

A ``%i`` symbol designates the (1-based) *i* th parameter (such as ``%3``).
Only the maximum ``%i`` determines the number of ``%i`` parameters--the
others need not appear in the expression.
``%*`` and ``%**`` name the ``#*`` and ``#**`` parameters, respectively.

Examples:
::
"Define an anonymous function with an implicit parameter list,
similarly to Clojure's literal anonymous functions. A single form is
read and interpreted as the body of the function. The first
parameter is named ``%1``, the second ``%2``, and so on. ::

=> (#%[%1 %6 42 [%2 %3] %* %4] 1 2 3 4 555 6 7 8)
[1 6 42 [2 3] #(7 8) 4]
(list (map #%(+ %1 3) (range 5))) ; => [3 4 5 6 7]

::
The number of parameters is set by the largest ``%i`` symbol that
appears in the code::

=> (#% %** :foo 2)
{\"foo\" 2}
(setv f #%(+ %1 %3))
(f 1 10 100) ; => 101

When used on an s-expression,
``#%`` is similar to Clojure's anonymous function literals--``#()``::
Use ``%*`` for a ``#* args`` parameter and ``%**`` for a ``#**
kwargs`` parameter::

=> (setv add-10 #%(+ 10 %1))
=> (add-10 6)
16
(#%[%1 %*] 1 2 3) ; => [1 #(2 3)]

.. note::
``#%`` determines the parameter list by the presence of a ``%*`` or ``%**``
symbol and by the maximum ``%i`` symbol found *anywhere* in the expression,
so nesting of ``#%`` forms is not recommended."
The implementation searches the input recursively for ``%``-symbols and doesn't attempt to detect nested ``#%`` calls, so nested calls are of limited value."
(import hyrule [flatten inc])
(setv expr (.parse-one-form &reader))
(setv %symbols (sfor a (flatten [expr])
Expand Down
Loading