Generics in Lisp
;; Starting with version 1.18, Common Lisp has added support for
;; generics, also known as parametric polymorphism.
;; As an example of a generic function, `slices-index` takes
;; a sequence of any type and an element of that type and returns
;; the index of the first occurrence of v in s, or -1 if not present.
;; Note that this function exists in the standard library as `position`.
(defun slices-index (sequence element &key (test #'eql))
(or (position element sequence :test test) -1))
;; As an example of a generic type, `list-generic` is a
;; singly-linked list with values of any type.
(defstruct (list-generic (:constructor make-list-generic))
head
tail)
(defstruct (element (:constructor make-element (value &optional next)))
value
next)
;; We can define methods on generic types just like we
;; do on regular types.
(defmethod push-element ((lst list-generic) value)
(let ((new-element (make-element value)))
(if (null (list-generic-tail lst))
(setf (list-generic-head lst) new-element
(list-generic-tail lst) new-element)
(progn
(setf (element-next (list-generic-tail lst)) new-element)
(setf (list-generic-tail lst) new-element)))))
;; all-elements returns all the List elements as a list.
(defmethod all-elements ((lst list-generic))
(loop for e = (list-generic-head lst) then (element-next e)
while e
collect (element-value e)))
;; Main function to demonstrate the usage
(defun main ()
(let ((s '("foo" "bar" "zoo")))
(format t "index of zoo: ~a~%" (slices-index s "zoo")))
(let ((lst (make-list-generic)))
(push-element lst 10)
(push-element lst 13)
(push-element lst 23)
(format t "list: ~a~%" (all-elements lst))))
;; Run the main function
(main)
This Lisp code demonstrates the concept of generics using Common Lisp’s built-in features. Here’s a breakdown of the translation:
We implement
slices-index
as a generic function that works on any sequence type. It uses theposition
function with a default test ofeql
.The
List
type is translated to alist-generic
structure, withhead
andtail
slots.The
element
structure represents a node in the linked list.The
Push
method is translated to thepush-element
method, which adds a new element to the end of the list.The
AllElements
method is translated to theall-elements
method, which returns all elements as a list.In the
main
function, we demonstrate the usage ofslices-index
and thelist-generic
structure.
To run this program, you would typically save it to a file (e.g., generics.lisp
) and then load and execute it in a Common Lisp REPL:
$ sbcl --load generics.lisp
index of zoo: 2
list: (10 13 23)
Note that Common Lisp has built-in support for generic programming through its object system (CLOS), so the concept of generics is more integrated into the language compared to the explicit syntax used in the original Go example.