Anaphoric Macros

New in version 0.9.12.

The anaphoric macros module makes functional programming in Hy very concise and easy to read.

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)

To use these macros you need to require the hy.extra.anaphoric module like so:

(require [hy.extra.anaphoric [*]])

These macros are implemented by replacing any use of the designated anaphoric symbols (it, in most cases) with a gensym. Consequently, it’s unwise to nest these macros, or to use an affected symbol as something other than a variable name, as in (print "My favorite Stephen King book is" 'it).

ap-if

Usage: (ap-if test-form then-form else-form)

As if, but the result of the test form is named it in the subsequent forms. As with if, the else-clause is optional.

=> (import os)
=> (ap-if (.get os.environ "PYTHONPATH")
...  (print "Your PYTHONPATH is" it))

ap-each

Usage: (ap-each xs body…)

Evaluate the body forms for each element it of xs and return None.

=> (ap-each [1 2 3] (print it))
1
2
3

ap-each-while

Usage: (ap-each-while xs pred 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.

=> (ap-each-while [1 2 3 4 5 6] (< it 4) (print it))
1
2
3

ap-map

Usage: (ap-map form xs)

Create a generator like map() that yields each result of form evaluated with it bound to successive elements of xs.

=> (list (ap-map (* it 2) [1 2 3]))
[2, 4, 6]

ap-map-when

Usage: (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.

=> (list (ap-map-when odd? (* it 2) [1 2 3 4]))
[2, 2, 6, 4]

=> (list (ap-map-when even? (* it 2) [1 2 3 4]))
[1, 4, 3, 8]

ap-filter

Usage: (ap-filter form xs)

The filter() equivalent of ap-map.

=> (list (ap-filter (> (* it 2) 6) [1 2 3 4 5]))
[4, 5]

ap-reject

Usage: (ap-reject form xs)

Equivalent to (ap-filter (not form) xs).

=> (list (ap-reject (> (* it 2) 6) [1 2 3 4 5]))
[1, 2, 3]

ap-dotimes

Usage: (ap-dotimes n body…)

Equivalent to (ap-each (range n) body…).

 => (setv n [])
 => (ap-dotimes 3 (.append n it))
 => n
[0, 1, 2]

ap-first

Usage: (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.

=> (ap-first (> it 5) (range 10))
6

ap-last

Usage: (ap-last form list)

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.

=> (ap-last (> it 5) (range 10))
9

ap-reduce

Usage: (ap-reduce form xs &optional initial-value)

This macro is an anaphoric version of reduce(). It works as follows:

  • Bind acc to the first element of xs, bind it to the second, and evaluate form.
  • Bind acc to the result, bind it to the third value of xs, and evaluate form again.
  • Bind acc to the result, and continue until xs is exhausted.

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

=> (ap-reduce (+ it acc) (range 10))
45

#%

Usage: #% expr

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 &rest and &kwargs parameters, respectively.

=> (#%[%1 %6 42 [%2 %3] %* %4] 1 2 3 4 555 6 7 8)
[1, 6, 42, [2, 3], (7, 8), 4]
=> (#% %** :foo 2)
{"foo": 2}

When used on an s-expression, #% is similar to Clojure’s anonymous function literals–#().

=> (setv add-10 #%(+ 10 %1))
=> (add-10 6)
16

#% 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.