World Playground Deceit.net

QoL addition to Common Lisp :start/:end


Thought I'd share a pretty cool "go go gadget" I added to the good old :start/:end keyword parameters found in various sequence and string functions (e.g. find): a duo of helpers to strictly augment their interface in order to change

;; Some ways to extract "cde"
(let ((str "abcdefg"))
  (subseq str (+ (position #\b str) 1) (position #\f str))
  (subseq str (position #\c str)       (+ (position #\e str) 1))
  (subseq str (search "cd" str)        (+ (search "de" str) 2)))

into

(let ((str "abcdefg"))
  (subseq str '(:after #\b) '(:before #\f))
  (subseq str '(:from #\c)  '(:until #\e))
  (subseq str '(:from "cd") '(:until "de")))

which I find greatly improves readability and helps prevent off-by-X errors. In general, I really like it when CL reads like a dialect of English, it's easy on the eye and the brain.

Their use is pretty streamlined too, as our wrapper's triviality shows:

(declaim (inline subseq))
(defun subseq (seq start &optional end (test #'eql))
  "Improved version of cl:subseq accepting %rel-start/end specs for :start/:end."
  (let* ((%start (%rel-start start seq :test test))
         (%end   (%rel-end   end   seq :test test :start %start)))
    (cl:subseq seq %start %end)))

A last little example to show the optional :last:

(subseq "https://foo.bar/page.html" '(:after :last #\/)) ;; => "page.html"

PS: I think this'll be very useful for the next Advent of Code, together with these reader extensions.