Skip to content

Latest commit

 

History

9 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Expanders

Welcome to Expanders!! :D

What is this?

This project lets you define expanders. An expander is like a namespace for forms that can be expanded by a macro.

It is 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)
(defvar plus-expander (make-expander))
(defvar minus-expander (make-expander))
;; Returns
MINUS-EXPANDER

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

(expanderp plus-expander)
;; Returns
T
(expanderp 'another-thing)
;; Returns
NIL
(expanderp minus-expander)
;; Returns
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))
;; Returns
OP

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

(expansionp plus-expander 'op)
;; Returns
T
(expansionp plus-expander 'hey)
;; Returns
NIL
(expansionp minus-expander 'op)
;; Returns
T

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

(documentation 'op plus-expander)
;; Returns
"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)))
;; Output
Old: "OP to - expansion"
New: "Another docstring"
;; Returns
NIL

We can expand an expansion using exp:expand:

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

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

(defmacro plus-macro (form)
  (expand plus-expander form))
;; Returns
PLUS-MACRO
(defmacro minus-macro (form)
  (expand minus-expander form))
;; Returns
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))
;; Returns
9
(minus-macro (op 5 4))
;; Returns
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 from getters.

Reference

Macro: exp:defexpansion (expander name macro-lambda-list &body body)

Define an expansion named NAME for EXPANDER. Arguments are specified in a macro-lambda-list where
&whole and &environment can be used.

Function: exp:expand (expander expr &optional env)

Expand an EXPANSION from EXPANDER. An environment object can be supplied.

Function: exp:expanderp (obj)

Check if an object is an expander

Function: exp:expansionp (expander name)

Retrieve the expansion function named NAME from EXPANDER. Return NIL if that function does not exist.

Function: exp:make-expander ()

Make an expander

About

Expanders for Common Lisp

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages