Interfaces in Lisp

Interfaces are named collections of method signatures.

(defpackage :geometry
  (:use :cl))

(in-package :geometry)

;; Here's a basic interface for geometric shapes.
(defgeneric area (shape)
  (:documentation "Calculate the area of a shape"))

(defgeneric perim (shape)
  (:documentation "Calculate the perimeter of a shape"))

;; For our example we'll implement this interface on
;; rect and circle types.
(defclass rect ()
  ((width :initarg :width :reader width)
   (height :initarg :height :reader height)))

(defclass circle ()
  ((radius :initarg :radius :reader radius)))

;; To implement an interface in Lisp, we just need to
;; define methods for the generic functions. Here we
;; implement area and perim on rects.
(defmethod area ((r rect))
  (* (width r) (height r)))

(defmethod perim ((r rect))
  (+ (* 2 (width r)) (* 2 (height r))))

;; The implementation for circles.
(defmethod area ((c circle))
  (* pi (expt (radius c) 2)))

(defmethod perim ((c circle))
  (* 2 pi (radius c)))

;; If a variable has a generic type, then we can call
;; methods that are defined for that generic function. Here's a
;; generic measure function taking advantage of this
;; to work on any shape.
(defun measure (shape)
  (format t "~A~%" shape)
  (format t "~A~%" (area shape))
  (format t "~A~%" (perim shape)))

(defun main ()
  (let ((r (make-instance 'rect :width 3 :height 4))
        (c (make-instance 'circle :radius 5)))
    ;; The circle and rect classes both
    ;; implement the area and perim generic functions so we can use
    ;; instances of these classes as arguments to measure.
    (measure r)
    (measure c)))

(main)

To run the program, save it to a file (e.g., interfaces.lisp) and use your Lisp interpreter. For example, if you’re using SBCL:

$ sbcl --script interfaces.lisp
#<RECT {1004A70103}>
12.0
14.0
#<CIRCLE {1004A70203}>
78.53981633974483d0
31.41592653589793d0

In this Lisp implementation:

  1. We define generic functions area and perim which serve as our “interface”.
  2. We create classes rect and circle to represent our shapes.
  3. We implement methods for area and perim for both rect and circle.
  4. The measure function takes any shape and calls area and perim on it.
  5. In the main function, we create instances of rect and circle and pass them to measure.

This implementation demonstrates how Lisp’s generic functions and methods provide a form of polymorphism similar to interfaces in other languages.