diff options
author | Colin Okay <okay@toyful.space> | 2020-12-13 11:01:10 -0600 |
---|---|---|
committer | Colin Okay <okay@toyful.space> | 2020-12-13 11:01:10 -0600 |
commit | 1c9c78d917d86632c176084352bbe90698a6c5da (patch) | |
tree | 735469dd7fa2b6d280b73b8c389630573e0d8de8 | |
parent | a2fcc41e193f68ce8cf5901523e9276723deb88a (diff) |
monadic>
-rw-r--r-- | functions.lisp | 4 | ||||
-rw-r--r-- | macros.lisp | 87 |
2 files changed, 90 insertions, 1 deletions
diff --git a/functions.lisp b/functions.lisp index 3c06f79..8296f87 100644 --- a/functions.lisp +++ b/functions.lisp @@ -7,7 +7,7 @@ (setf arg (funcall fn arg)))) (defun all> (arg &rest preds) - "Predicate Filter. Returns ARG if (PRED ARG) is non-NIL for each + "Predicate Filter. Returns ARG if (PRED ARG) is non-NIL for every PRED in PREDS" (dolist (pred preds arg) (unless (funcall pred arg) @@ -15,6 +15,8 @@ PRED in PREDS" (defun some> (arg &rest preds) + "Predicate filter. Returns ARG if (PRED ARG) is non-NIL for any PRED +in PREDS." (dolist (pred preds nil) (when (funcall pred arg) (return-from some> arg)))) diff --git a/macros.lisp b/macros.lisp index 6381101..c4aa0aa 100644 --- a/macros.lisp +++ b/macros.lisp @@ -56,6 +56,44 @@ ;; is not ok. (defmacro $ ((&optional (prefix "$")) expr) + "Function splicer. A kind of partial evaluation. + +Meant to be used in conjunction with the reader macro #$. + +E.g. + +#$(+ $X 1) + +is roughly equivalent to + +(LAMBDA ($X) (+ $X 1)) + +The order of arguments can be controlled by using positional +variables. E.g. + +#$(+ $2 $1) + +is equivalent to + +(LAMBDA ($1 $2) (+ $2 $1)) + +Limited nestiing is supported. E.g. + +#$(MAPCAR #$$(CONS $$INNER (LENGTH $OUTER)) $OUTER) + +is equvalent to + +(LAMBDA ($OUTER) + (MAPCAR (LAMBDA ($$INNER) (CONS $$INNER (LENGTH $OUTER))) + $OUTER)) + +However, a variable inside a nested form must actually appear in the +surrounding form. + +THIS WONT WORK: #$(+ #$$(* $X $$Y)) because $$Y doesn't appear in the +surrounding form. + + " (let ((new-params (list)) (numeric-params nil)) (labels ((walk (node) @@ -86,6 +124,7 @@ (defmacro conj (&rest preds) + "A composition macro. Short circuiting predicate conjunction." (let ((block-label (gensym))) `(let ((preds (list ,@preds))) (lambda (arg) @@ -96,7 +135,9 @@ (setf acc (funcall p arg)) (unless acc (return-from ,block-label nil))))))))) + (defmacro disj (&rest preds) + "A composition macro. Short circuiting predicate disjunction." (let ((block-label (gensym))) `(let ((preds (list ,@preds))) (lambda (arg) @@ -108,6 +149,17 @@ (when acc (return-from ,block-label acc))))))))) (defmacro make-lazy (form) + "Wraps FORM in a thunk. Intended to be used with teh #~ and #! reader macros: + +(let ((computation #~(progn (print 'hey) 10))) + (cons #!computation #!computation)) + +HEY +(10 . 10) + +The first time the computation is forced, it is run, and HEY is +printed. But the next time only the return value is used. +" (let ((run-p (gensym)) (val (gensym))) `(let ((,run-p nil) @@ -120,3 +172,38 @@ + + +(defmacro monadic> ((init &key (fail-when #'null) fail-with) &rest functions) + "A threading macro. Some examples: + +(monadic> + (\"hey dude what's the big idea\") ; starting state + #$(values (search \"the\" $s) $s) ; multiple-values are passed along as arguments + #$(subseq $2 $1)) : returns the result of the last form + +should return \"the big idea\" + +(monadic> + (\"hey dude what's the big idea?\" :fail-with \"☹\") + #$(values (search \"NOOOOOOPE\" $s) $s) + #$(subseq $2 $1)) + +should return \"☹\". + +FAIL-WHEN should be a function, a predicate, that operates on the +first value returned from one of the forms. If non-NIL, the MONADIC> +form returns with the value of the expression FAIL-WITH . +" + (let ((vals (gensym)) + (fn (gensym)) + (block-label (gensym))) + `(let ((,vals (multiple-value-list ,init))) + (block ,block-label + (dolist (,fn (list ,@functions) (values-list ,vals)) + (setq ,vals (multiple-value-list (apply ,fn ,vals))) + (when (funcall ,fail-when (car ,vals)) + (return-from ,block-label ,fail-with))))))) + + + |