diff --git a/flan-utils.lisp b/flan-utils.lisp index 0e36978..92b11f9 100644 --- a/flan-utils.lisp +++ b/flan-utils.lisp @@ -107,6 +107,8 @@ (defun true (f) (not (null f))) + ; Functional + (defun nop () "Do nothing" nil) @@ -115,6 +117,42 @@ "Do nothing" t) + ; Combinators + + (defun deatomise (list) + "If `list' is a list, pass it through; if it is a non-nil atom, wrap it in a single-element list" + (if (and list (atom list)) + (cons list nil) + list)) + + (defun deatomise! (list) "Ensure `list' is a list" (cons list nil)) + + (defun combine (fa fb &key (pass #'deatomise!)) + "Returns an applicative lambda that runs (`fb' (`fa' args...)...) +NOTE: This function *applies* the result of `fa' to `fb', therefore, if the result of `fa' is a list, the elements of said list are applied as the sequential arguments for `fb', if the result is a single element, it is passed as argument 1 only (this includes ``nil''). If you want to pass the result of `fa' to `fb' verbatim through the first argument only, use ``combine1''(). +WARNING: By default, if `fa' returns ``nil'', the ``nil'' is passed as argument 1 to `fb'. Therefore, no function `fa' will ever produce 0 arguments for `fb'; if you wish to override this behaviour and allow a nil return to mean 0 arguments, set `pass' to ``deatomise''() (to still ensure the return of `fa' is contained list; you can use ``combine!''() for behaviour instead too), or the ``identity''() function, if you know `fa' returns a list." + (lambda (&rest args) (apply fb (funcall pass (apply fa args))))) + + (defun combine! (fa fb) + "Returns an applicative lambda that runs (`fb' [(`fa' args...)...]) +NOTE: This is the same as calling `combine' with `pass' as ``deatomise''()." + (combine fa fb :pass #'deatomise)) + + (defun combine1 (fa fb) + "Returns a lambda that runs (`fb' (`fa' args...)) +NOTE: The difference between this an ``combine''() is that `combine' *applies* the result of `fa' to `fb', whereas `combine1'() simply calls `fb' with the result of `fa'." + (lambda (&rest args) (funcall fb (apply fa args)))) + + (defun inverse (func) + "Returns a lambda that resolves ¬(`func' args...)" + (lambda (&rest n) (not (apply func n)))) + + (defun inverse* (&rest functions) + "Returns a list of `inverse'()d functions from `functions'" + (mapcar #'inverse functions)) + + ;; Mapping + (defun mapline (input fi &key (read-line #'read-line)) "Map lines from stream" (loop for line = (funcall read-line input nil)