chain
2026-01-01
Chaining/threading macros, one of them `setf`ing its first argument
Upstream URL
Author
License
Chain
Migrated to Codeberg
Table of Contents
[in package CHAIN]
- [system] "chain"
- Version: 0.4.0
- Description: Chaining/threading macros, one of them
setfing its first argument - Licence: BSD-3
- Author: Thomas Bartscher thomas-bartscher@weltraumschlangen.de
- Depends on: metabang-bind, mgl-pax
1 Reference
-
[macro] => ARGUMENT &BODY BODY
Thread a value through a series of transformations, where
<>represents the value of the last transformation.-
Arguments
-
argumentInitial value. Supports
:letas in the body. -
bodyForms to apply sequentially. Each form has
<>bound as an anaphoric variable bound to the return value of the previous form in the body. Each form may be:-
A symbol
This is taken to be the name of a function, and called on the result of the previous form
Note that
<>as a bare symbol form also will be interpreted in this way, and not as a variable. -
A regular form
Evaluated normally. Gets its semantics from
<>being bound to the result of the previous form. -
(:let [var] [form])Binds
[var]to the result of[form]in the remainder of the chain. -
(:call [funcallable])[funcallable]needs to evaluate to a funcallable object. Calls that object onto the previous value.Can be understood as
(funcall [funcallable] <>) -
(:req [form])If
[form]returns nil, the whole macro call will short-circuit and return nil without evaluating further forms. If[form]returns a non-nil value, that value is taken to be the value of the:reqform.Can be understood as
(or [form] (bail)) -
(:req [predicate] [form])Evaluates
[form]as usual, then checks whether[predicate]returns nil on the result. If it does, short circuit and returnnilfrom=>, otherwise continue with the computed value.Can be understood as
(let ((value [form])) (if ([predicate] value) value (bail))) -
(:cond [form])If the
[form]returns nil, the value of the:condform is taken to be the value of the previous form. Otherwise the value of the:condform is the value of[form].Can be understood as
(or [form] <>) -
(:cond [predicate] [form])Evaluates
[form]as usual, then checks whether[predicate]returns nil on the result. If it does, return the value of the previous form, otherwise continue with the computed value.Can be understood as
(let ((value [form])) (if ([predicate] value) value <>)) -
(lambda ([argument]) [body])This is called on the result of the previous form.
:let,:req, and:condcan be nested directly in each other.:callcan be nested inside the other forms.Inside
bodythebailandfinishmacros are defined. They allow to return from the=>form early.bailtakes an optional andfinisha required argument for a return value.finishis supplied for easier switching between=>andset=>.Note that, unlike other chaining/threading macros,
=>does not support something like this:!(=> '(1 2 3) ! (remove-if #'evenp) ! (mapcar #'-))This would not compile, with
remove-ifandmapcarnot being supplied enough arguments. -
-
-
Return value
Returns the value of its last form, or, should
bailorfinishbe called, the value of their argument, defaulting tonilforbail. -
Examples
;;; basic usage (=> 5 (* <> 2) ; => 10 1+) => 11 (defun sigmoid (x) (=> x - exp 1+ /)) (sigmoid 0) => 0.5 ;;; lambda form usage (=> 5 (lambda (x) ; => 8 (+ x 3)) (* <> 2)) => 16 ;;; using `:let` (defun triangle (n) (=> (:let a n) 1+ (* <> a) (/ <> 2))) (triangle 10) => 55 ;;; using `:call` (defun f^2 (f x) (=> x (:call f) (:call f))) (f^2 (lambda (x) (* x x)) 10) => 10000 ;;; using `:req` (defun negate-first-even (xs) (=> xs (:req (first (member-if #'evenp <>))) -)) (negate-first-even '(1 2 3)) => -2 (negate-first-even '(1 3)) => NIL (defun maybe-halve (x) (=> x (:req integerp (/ <> 2)))) (maybe-halve 3) => NIL (maybe-halve 4) => 2 ;;; using `:cond` (defun negate-first-prefer-even (xs) (=> xs (:cond (member-if #'evenp <>)) first -)) (negate-first-prefer-even '(1 2 3)) => -2 (negate-first-prefer-even '(1 3)) => -1 (defun halve-even (x) (=> x (:cond integerp (/ <> 2)))) (halve-even 3) => 3 (halve-even 4) => 2 ;;; using `bail` (defun halve-and-inc-maybe (x) (=> x (if (evenp <>) (/ <> 2) (bail)) 1+)) (halve-and-inc-maybe 3) => NIL (halve-and-inc-maybe 4) => 3 ;;; `finish` examples can be derived by replacing `(bail)` with ;;; `(finish nil)` in the `bail` examples.
-
-
[macro] SET=> ARGUMENT &BODY BODY
Thread the initial value of a place through transformations like in macro
=>, and update the place with the final result.-
Arguments
-
Return value
Returns the value of its last form.
-
Side effects
Updates PLACE to the calculated result.
-
Examples
(defstruct box value) ;;; basic usage (let ((x 5)) (set=> x (* <> 2) ; => 10 1+) ; => 11 x) => 11 ;;; using `:let` (defun triangle! (box) (set=> (:let a (box-value box)) 1+ (* <> a) (/ <> 2))) (let ((box (make-box :value 10))) (triangle! box) (box-value box)) => 55 ;;; using `:req` (defun maybe-only-even! (box) (set=> (box-value box) (:req (remove-if-not #'evenp <>)))) (let ((box (make-box :value '(1 2 3 4 5 6)))) (maybe-only-even! box) (box-value box)) => (2 4 6) (let ((box (make-box :value '(1 3 5)))) (maybe-only-even! box) (box-value box)) => (1 3 5) (defun maybe-halve! (box) (set=> (box-value box) (:req integerp (/ <> 2)))) (let ((box (make-box :value 3))) (maybe-halve! box) (box-value box)) => 3 (let ((box (make-box :value 4))) (maybe-halve! box) (box-value box)) => 2 ;;; using `bail` (defun double-odd-inc!-or-zero (box) (set=> (box-value box) (if (oddp <>) (* <> 2) (bail 0)) 1+)) (let ((box (make-box :value 2))) (values (double-odd-inc!-or-zero box) (box-value box))) => 0 => 2 (let ((box (make-box :value 3))) (values (double-odd-inc!-or-zero box) (box-value box))) => 7 => 7 ;;; using `finish` (defun double-odd-inc-or-zero! (box) (set=> (box-value box) (if (oddp <>) (* <> 2) (finish 0)) 1+)) (let ((box (make-box :value 2))) (values (double-odd-inc-or-zero! box) (box-value box))) => 0 => 0 (let ((box (make-box :value 3))) (values (double-odd-inc-or-zero! box) (box-value box))) => 7 => 7
-
-
[macro] LAMBDA=> &BODY BODY
Returns a lambda taking one argument. The argument is threaded through the
bodylike in=>. Supports all the special forms that=>does.-
Examples
(funcall (lambda=> - exp 1+ /) 0) => 0.5
-
-
[macro] DEF=> NAME &BODY BODY
Defines a function named
nametaking one argument. The argument is threaded through thebodylike in=>. Supports all the special forms that=>does.-
Examples
(def=> logistic-curve - exp 1+ /) (logistic-curve 0) => 0.5
-
-
[macro] LAMBDA+> (MAIN-ARG &REST ARGS) &BODY BODY
Version of
lambda=>with explicitly supplied arguments. More general since it can accept more than one argument. The first argument is the argument to the=>call.
-
[macro] DEF+> NAME (MAIN-ARG &REST ARGS) &BODY BODY
Version of
def=>with explicitly supplied arguments. More general since it can accept more than one argument. The first argument is the argument to the=>call.