Skip to content

Latest commit

 

History

History
219 lines (172 loc) · 5.47 KB

README.md

File metadata and controls

219 lines (172 loc) · 5.47 KB

Expanders

Welcome to Expanders!! :D

What is this?

The project lets you define expanders. An expander is like a namespace for forms that should transform in a specific way.

It easier to understand with an example. Suppose we have the following form (op (+ 3 4) b). This form will be used in two different macros named plus-macro and minus-macro. Each macro receives a form. That form could start with op. In that case, op is substituted by + or - respectively.

Let's define 2 different expanders:

;; We assume (use-package #:expanders)
(defexpander plus-expander)
(defexpander minus-expander)
minus-expander

Now we have two different expanders. The function exp:expanderp can tell us if a symbol denotes an expander:

(expanderp 'plus-expander)
t
(expanderp 'hey)
nil
(expanderp 'minus-expander)
t

Now it is time to define the expansion op for each expander using exp:defexpansion:

(defexpansion plus-expander op (a b)
  "OP to + expansion"
  `(+ ,a ,b))

(defexpansion minus-expander op (a b)
  "OP to - expansion"
  `(- ,a ,b))
op

We can check if a symbol is an expansion for a given expander using exp:expansionp:

(expansionp 'plus-expander 'op)
t
(expansionp 'plus-expander 'hey)
nil
(expansionp 'minus-expander 'op)
t

Also, we can retrieve or set the docstring using documentation:

(documentation 'op 'plus-expander)
"OP to + expansion"
(let ((old-docstring (documentation 'op 'minus-expander)))
  (setf (documentation 'op 'minus-expander) "Another docstring")
  (let ((new-docstring (documentation 'op 'minus-expander)))
    (format t "Old: ~s~%New: ~s" old-docstring new-docstring)))
Old: "OP to - expansion"
New: "Another docstring"
nil

We can expand a form using exp:expand. Note that the form must be a list starting with the name of an expansion:

(expand 'plus-expander '(op 3 (+ 5 6)))
(+ 3 (+ 5 6))
(expand 'minus-expander '(op 3 (+ 5 6)))
(- 3 (+ 5 6))

Finally, let's define the macros plus-macro and minus-macro:

(defmacro plus-macro (form)
  (if (and (consp form)
           (expansionp 'plus-expander (car form)))
      (expand 'plus-expander form)
      form))
plus-macro
(defmacro minus-macro (form)
  (if (and (consp form)
           (expansionp 'minus-expander (car form)))
      (expand 'minus-expander form)
      form))
minus-macro

If we use the form (op 5 4) we will see that each macro will expand to (+ 5 4) or (- 5 4) respectively.

(plus-macro (op 5 4))
9
(minus-macro (op 5 4))
1

Why?

  • It is common: I have noticed that having expanders is a relatively common pattern in macros. The best example is setf and its setf-expanders. Another project using expanders is CFFI and its type parsers. In my own projects I ended up using the same techniques (Clith).
  • Duality of syntax: We can increase the duality of syntax using expanders. The best example is setf. Thanks to setf we don't need names for setters because they come for free with the getter.

Reference

Macro: exp:defexpander (sym)

Defines an expander represented by the symbol SYM.

Macro: exp:defexpansion (expander name (&rest args) &body body)

Defines an expansion for the expander EXPANDER. NAME must be a symbol denoting
the new expansion. ARGS is a destructuring lambda list. This must return the desired
expansion for NAME and EXPANDER.

Function: exp:expand (expander form)

Expands a form using an expander. FORM must be a list starting with a valid
expansion symbol for the expander EXPANDER.

Function: exp:expanderp (sym)

Check if a symbol denotes an expander.

Function: exp:expansionp (expander expansion)

Checks if EXPANSION is a valid expansion for the expander EXPANDER.
EXPANDER must be a valid expander.