Lisp - :around Method Combination



:around is one of the standard method combination in CLOS.:around is a powerful way to execute code before and after the primary method, in order to create a wrapper around a generic function call. In this chapter, we're discussing how to create and apply functions using :around with a running example.

Defining :around function

An :around function is defined using following syntax

; define a generic function
(defgeneric process-data (data))

; define an around function
(defmethod process-data :around ((data t))
   ; call statement before calling the primary function
   (format t "Starting processing data: ~a~%" data)
   (let ((result (call-next-method))) ; call the primary function
      ; print statement after calling the primary function   
      (format t "Finished processing data: ~a, result: ~a~%" data result) 
    result))
  • defmethod process-data :around ((data t)) − around function is defined on a generic function process-data.

  • call-next-method − this call is used to invoke next applicable method either another around function or the primary function. call-next-method returned value can be used by around method which can be modified or used.

By placing code before and after call-next-method call, we are effectively wrapping the execution of next function call.

Applications of :around function

  • Log or trace function calls.

  • Cache a result.

  • Validate argument

  • Modify returned value(s)

  • Handle error(s)

Example - Use of :around Function

Following is the complete example of a :around function.

main.lisp

; define a generic function
(defgeneric process-data (data))

; define a around function
(defmethod process-data :around ((data t))
  (format t "Starting processing data: ~a~%" data)
  (let ((result (call-next-method)))
    (format t "Finished processing data: ~a, result: ~a~%" data result)
    result))

; define integer specific implementation
(defmethod process-data ((data integer))
  (format t "Processing integer data: ~a~%" data)
  (* data 2))

; define string specific implementation
(defmethod process-data ((data string))
  (format t "Processing string data: ~a~%" data)
  (concatenate 'string data " processed"))

(format t "~a~%" (process-data 10))
(format t "~a~%" (process-data "hello "))

Output

When you execute the code, it returns the following result −

Starting processing data: 10
Processing integer data: 10
Finished processing data: 10, result: 20
20
Starting processing data: hello 
Processing string data: hello 
Finished processing data: hello , result: hello  processed
hello  processed

Explanation

When a generic function is called , the around function is called before and after execution of generic function as demonstrated above.

  • When process-data 10 called, then :around method is executed before it.

  • A starting message is printed as Starting processing data: 10.

  • call-next-method is called to execute integer specific method.

  • Processing integer data: 10 is printed. Processed value 20 is returned.

  • A finishing message is printed as Finished processing data: 10, result: 20

  • Same process is repeated for string based generic function.

Advertisements