Image('in.png') .resize(200, 100) .erode() .save('out.jpg');
In Lisp, there is not such thing as a dot notation to call an object method. Methods are functions taking the object as first argument. To mimic the dot operator that allows chaining we would like to write:
($ (image "in.png") ; Note: . is not a valid name, we use $ instead (resize 200 100) (erode) (save "out.jpg"))
Hopefully, Lisp allows to rewrite the previous snippet into code that actually works with macros.
With temporary variables
The first way to rewrite it is with a serie of assignement. It uses a temporary variable that is being passed along.
progn is being used to group the actions into a single block that returns the
(progn (defvar tmp (image "in.png")) (setf tmp (resize tmp 200 100)) (setf tmp (erode tmp)) (setf tmp (save tmp "out.jpg")) tmp)
And this is the macro that makes it work.
(defmacro $ (object &rest actions) (let ((curr-object (gensym))) (concatenate 'list '(progn) (list `(defvar ,curr-object ,object)) (loop for action in actions collect `(setf ,curr-object (,(car action) ,curr-object ,@(cdr action)))) (list `,curr-object))))
Some keys to understand it if you don't know lisp macros.
`set the following as output code
,evaluate the code
@expand the list.
(resize @(200 100)) -> (resize 200 100)
gensymcreates a local variable with a unique name
caris the first element of the list,
cdris the rest
The previous way was probably how would have written it in your code. Since we are programmaticaly rewriting the operation, we do not care about how readable the output is. We can remove the use of the temporary variable inlining the calls.
(save (erode (resize (image "in.png") 200 100)) "out.jpg")
The macros that powers it is much smaller.
(defmacro $ (object &rest actions) (let ((res `,object)) (loop for action in actions do (setf res `(,(car action) ,res ,@(cdr action)))) res))