pub const PRELUDE: &str = r#"
;; Thread-first: inserts val as the FIRST argument of each form
;; (-> 5 (+ 3) (* 2)) => (* (+ 5 3) 2) => 16
(defmacro -> (val . forms)
(if (null? forms)
val
(let ((form (car forms))
(rest (cdr forms)))
(if (list? form)
`(-> (,(car form) ,val ,@(cdr form)) ,@rest)
`(-> (,form ,val) ,@rest)))))
;; Thread-last: inserts val as the LAST argument of each form
;; (->> (range 10) (filter odd?) (map square)) => (map square (filter odd? (range 10)))
(defmacro ->> (val . forms)
(if (null? forms)
val
(let ((form (car forms))
(rest (cdr forms)))
(if (list? form)
`(->> (,(car form) ,@(cdr form) ,val) ,@rest)
`(->> (,form ,val) ,@rest)))))
;; Thread-as: binds val to a name, allowing arbitrary placement
;; (as-> 5 x (+ x 3) (* x x) (- x 1)) => 63
(defmacro as-> (val name . forms)
(if (null? forms)
val
(let ((form (car forms))
(rest (cdr forms)))
`(let ((,name ,val))
(as-> ,form ,name ,@rest)))))
;; Conditional thread-first: short-circuits on nil
;; (some-> m :key :nested) => nil if any step returns nil
(defmacro some-> (val . forms)
(if (null? forms)
val
(let ((form (car forms))
(rest (cdr forms)))
(if (list? form)
`(let ((v# ,val))
(if (nil? v#) nil (some-> (,(car form) v# ,@(cdr form)) ,@rest)))
`(let ((v# ,val))
(if (nil? v#) nil (some-> (,form v#) ,@rest)))))))
;; when-let: bind a value, execute body only if non-nil
;; (when-let (x (get m :key)) (println x))
(defmacro when-let (binding . body)
(let ((var (car binding))
(expr (cadr binding)))
`(let ((,var ,expr))
(when (not (nil? ,var))
,@body))))
;; if-let: bind a value, branch on nil/non-nil
;; (if-let (x (get m :key)) (use x) (default))
(defmacro if-let (binding then else)
(let ((var (car binding))
(expr (cadr binding)))
`(let ((,var ,expr))
(if (nil? ,var) ,else ,then))))
"#;