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:

  1. We implement slices-index as a generic function that works on any sequence type. It uses the position function with a default test of eql.

  2. The List type is translated to a list-generic structure, with head and tail slots.

  3. The element structure represents a node in the linked list.

  4. The Push method is translated to the push-element method, which adds a new element to the end of the list.

  5. The AllElements method is translated to the all-elements method, which returns all elements as a list.

  6. In the main function, we demonstrate the usage of slices-index and the list-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.