Skip to content

Commit

Permalink
Feat: if-not, if-not-let
Browse files Browse the repository at this point in the history
If-not-let code, documentation, tests adapted from alexandria.
  • Loading branch information
kilianmh committed Jun 16, 2024
1 parent 4735a7c commit d7d6073
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 0 deletions.
38 changes: 38 additions & 0 deletions binding.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -324,3 +324,41 @@ Also, this version makes the bindings immutable."
`(function ,(car binding)))
bindings)))
,@body))

(defmacro if-not (test then &optional else)
"If TEST evaluates to NIL, evaluate THEN and return its values,
otherwise evaluate ELSE and return its values. ELSE defaults to NIL."
`(if (not ,test)
,then
,else))

(defmacro if-not-let (bindings &body (then-form &optional else-form))
"Creates new variable bindings, and conditionally executes either
THEN-FORM or ELSE-FORM. ELSE-FORM defaults to NIL.
BINDINGS must be either single binding of the form:
(variable initial-form)
or a list of bindings of the form:
((variable-1 initial-form-1)
(variable-2 initial-form-2)
...
(variable-n initial-form-n))
All initial-forms are executed sequentially in the specified order. Then all
the variables are bound to the corresponding values.
If one of the variables was bound to NIL, the THEN-FORM is executed with the
bindings in effect, otherwise the ELSE-FORM is executed with the bindings in
effect.
Adapted from Alexandria if-let."
(let* ((binding-list (if (and (consp bindings) (symbolp (car bindings)))
(list bindings)
bindings))
(variables (mapcar #'car binding-list)))
`(let ,binding-list
(if-not (and ,@variables)
,then-form
,else-form))))
2 changes: 2 additions & 0 deletions package.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@
#:lret
#:lret*
#:and-let*
#:if-not
#:if-not-let
#:recklessly-continue
#:static-binding-flush-error
#:static-binding-flush-error-group
Expand Down
42 changes: 42 additions & 0 deletions tests/binding.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,45 @@
(eval `(and-let* ((x 1)
(y 2))
x))))

(test if-not
(is (= 2 (if-not t 1 2)))
(is (= 2 (if-not "test" 1 2)))
(is (= 1 (if-not nil 1 2)))
(is (null (if-not t 1))))

;; tests adapted from Alexandria/tests
(declaim (notinline opaque))
(defun opaque (x)
x)

(test if-not-let
(is (eql (if-not-let (x (opaque :ok))
:bad
x)
:ok))
(is (eql :ok
(if-not-let (x (opaque nil))
(and (not x) :ok)
:bad)))
(is (= 3
(let ((x 1))
(if-not-let ((x 2)
(y x))
:oops
(+ x y)))))
(is (= 1
(if-not-let ((x 1)
(y nil))
(and (not y) x)
:oops)))
(is (if-not-let (x)
(not x)
:oops))
(is (eql :type-error
(handler-case
(eval '(if-not-let x
:oops
:oops))
(type-error ()
:type-error)))))

0 comments on commit d7d6073

Please sign in to comment.