ruricolist / serapeum

Utilities beyond Alexandria
MIT License
415 stars 41 forks source link

Interest in adding `prepend` and `prependf`? #142

Closed TheLostLambda closed 1 year ago

TheLostLambda commented 1 year ago

Hello! I'd love to contribute some code to serapeum but wanted to get a feel for what others think before drafting this up in a PR.

Would there be interest in adding prepend and prependf definitions that act just like their append counterparts (appendf is defined by alexandria) but with their arguments reversed.

This obviously doesn't do too much good when you're just appending two lists — you could just swap the arguments after all:

CL-USER> (append '(1 2 3) '(4 5 6)) 
(1 2 3 4 5 6)
CL-USER> (prepend '(4 5 6) '(1 2 3)) 
(1 2 3 4 5 6)

But I suppose is makes a bit of a difference with more than one list, since more things need to be swapped:

CL-USER> (append '(1 2) '(3 4) '(5 6)) 
(1 2 3 4 5 6)
CL-USER> (prepend '(5 6) '(3 4) '(1 2)) 
(1 2 3 4 5 6)

I found this most helpful though when using prependf — when using appendf you can no longer swap arguments to get the right order since the first argument is the place that everything ends up:

CL-USER> (defparameter *place* '(4 5 6))
*PLACE*
CL-USER> (appendf *place* '(1 2 3))
(4 5 6 1 2 3) ; Wrong!!!

;;; Now with `prependf`...

CL-USER> (defparameter *place* '(4 5 6))
*PLACE*
CL-USER> (prependf *place* '(1 2 3))
(1 2 3 4 5 6) ; Right :)

Of course, you could use setf, but this quickly becomes unwieldy when your place is a longer expression (note that you can't just put (elt crates (1- to)) in a let — that breaks setf and you'd need at least macrolet):

;; Yikes!
(setf (elt crates (1- to)) (append moved (elt crates (1- to))))
;; Much nicer :)
(prependf (elt crates (1- to) moved))

The definitions are incredibly simple, so I'm not too worried about bugs or implementation issues:

(defun prepend (&rest lists)
  (apply #'append (reverse lists)))

(define-modify-macro prependf (&rest args)
  prepend)

Let me know if I should open a PR with those two definitions added and some documentation written!

ruricolist commented 1 year ago

I can definitely see the argument for prependf being more ergonomic. And since there's already a qprepend operation for queues there's an argument from consistency to have prepend for lists as well.

For queues, there's also a qpreconc operation that's the destructive equivalent; it might be worth having preconc and preconcf for lists as well, using nconc instead of append.

TheLostLambda commented 1 year ago

Thanks for the swift reply! I can get behind adding preconc and preconcf as well — with that being said, I've struggled a little bit to see how appendf and nconcf differ in their effects (since the -f versions are always destructive) — I'd imagine there is some efficiency difference and maybe it looks like nconcf doesn't play as nice with circular lists?

But that's just an aside I suppose — I'll get to drafting a PR with those four new definitions!

TheLostLambda commented 1 year ago

Just as a heads up for @ruricolist , I think that failing ECL CI might mean that new documentation isn't being generated? It doesn't look like any of the prepend changes have made it into the REFERENCE.md file!