A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from http://blog.vjeux.com/2011/lisp/lisp-chaining-operator.html below:

Lisp – Chaining Operator

In the Javascript world, it is a common thing to chain methods call. For example, this could be a call from an image processing library.

Image('in.png')
  .resize(200, 100)
  .erode()
  .save('out.jpg');

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"))

($ (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 tmp value.

(progn
  (defvar tmp (image "in.png"))
  (setf tmp (resize tmp 200 100))
  (setf tmp (erode tmp))
  (setf tmp (save tmp "out.jpg"))
  tmp)

(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))))

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

Inline

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")

(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))

(defmacro $ (object &rest actions) (let ((res `,object)) (loop for action in actions do (setf res `(,(car action) ,res ,@(cdr action)))) res))

Conclusion

I took a popular design pattern on the Javascript world and adapted it to lisp. It makes writing several chained method calls easier.


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.3