161 lines
		
	
	
	
		
			6.5 KiB
		
	
	
	
		
			Common Lisp
		
	
	
	
	
	
			
		
		
	
	
			161 lines
		
	
	
	
		
			6.5 KiB
		
	
	
	
		
			Common Lisp
		
	
	
	
	
	
(in-package :alexandria)
 | 
						|
 | 
						|
;;; To propagate return type and allow the compiler to eliminate the IF when
 | 
						|
;;; it is known if the argument is function or not.
 | 
						|
(declaim (inline ensure-function))
 | 
						|
 | 
						|
(declaim (ftype (function (t) (values function &optional))
 | 
						|
                ensure-function))
 | 
						|
(defun ensure-function (function-designator)
 | 
						|
  "Returns the function designated by FUNCTION-DESIGNATOR:
 | 
						|
if FUNCTION-DESIGNATOR is a function, it is returned, otherwise
 | 
						|
it must be a function name and its FDEFINITION is returned."
 | 
						|
  (if (functionp function-designator)
 | 
						|
      function-designator
 | 
						|
      (fdefinition function-designator)))
 | 
						|
 | 
						|
(define-modify-macro ensure-functionf/1 () ensure-function)
 | 
						|
 | 
						|
(defmacro ensure-functionf (&rest places)
 | 
						|
  "Multiple-place modify macro for ENSURE-FUNCTION: ensures that each of
 | 
						|
PLACES contains a function."
 | 
						|
  `(progn ,@(mapcar (lambda (x) `(ensure-functionf/1 ,x)) places)))
 | 
						|
 | 
						|
(defun disjoin (predicate &rest more-predicates)
 | 
						|
  "Returns a function that applies each of PREDICATE and MORE-PREDICATE
 | 
						|
functions in turn to its arguments, returning the primary value of the first
 | 
						|
predicate that returns true, without calling the remaining predicates.
 | 
						|
If none of the predicates returns true, NIL is returned."
 | 
						|
  (declare (optimize (speed 3) (safety 1) (debug 1)))
 | 
						|
  (let ((predicate (ensure-function predicate))
 | 
						|
	(more-predicates (mapcar #'ensure-function more-predicates)))
 | 
						|
    (lambda (&rest arguments)
 | 
						|
      (or (apply predicate arguments)
 | 
						|
	  (some (lambda (p)
 | 
						|
		  (declare (type function p))
 | 
						|
		  (apply p arguments))
 | 
						|
		more-predicates)))))
 | 
						|
 | 
						|
(defun conjoin (predicate &rest more-predicates)
 | 
						|
  "Returns a function that applies each of PREDICATE and MORE-PREDICATE
 | 
						|
functions in turn to its arguments, returning NIL if any of the predicates
 | 
						|
returns false, without calling the remaining predicates. If none of the
 | 
						|
predicates returns false, returns the primary value of the last predicate."
 | 
						|
  (if (null more-predicates)
 | 
						|
      predicate
 | 
						|
      (lambda (&rest arguments)
 | 
						|
	(and (apply predicate arguments)
 | 
						|
	     ;; Cannot simply use CL:EVERY because we want to return the
 | 
						|
	     ;; non-NIL value of the last predicate if all succeed.
 | 
						|
	     (do ((tail (cdr more-predicates) (cdr tail))
 | 
						|
		  (head (car more-predicates) (car tail)))
 | 
						|
		 ((not tail)
 | 
						|
		  (apply head arguments))
 | 
						|
	       (unless (apply head arguments)
 | 
						|
		 (return nil)))))))
 | 
						|
 | 
						|
 | 
						|
(defun compose (function &rest more-functions)
 | 
						|
  "Returns a function composed of FUNCTION and MORE-FUNCTIONS that applies its
 | 
						|
arguments to to each in turn, starting from the rightmost of MORE-FUNCTIONS,
 | 
						|
and then calling the next one with the primary value of the last."
 | 
						|
  (declare (optimize (speed 3) (safety 1) (debug 1)))
 | 
						|
  (reduce (lambda (f g)
 | 
						|
	    (let ((f (ensure-function f))
 | 
						|
		  (g (ensure-function g)))
 | 
						|
	      (lambda (&rest arguments)
 | 
						|
		(declare (dynamic-extent arguments))
 | 
						|
		(funcall f (apply g arguments)))))
 | 
						|
          more-functions
 | 
						|
          :initial-value function))
 | 
						|
 | 
						|
(define-compiler-macro compose (function &rest more-functions)
 | 
						|
  (labels ((compose-1 (funs)
 | 
						|
             (if (cdr funs)
 | 
						|
                 `(funcall ,(car funs) ,(compose-1 (cdr funs)))
 | 
						|
                 `(apply ,(car funs) arguments))))
 | 
						|
    (let* ((args (cons function more-functions))
 | 
						|
           (funs (make-gensym-list (length args) "COMPOSE")))
 | 
						|
      `(let ,(loop for f in funs for arg in args
 | 
						|
		   collect `(,f (ensure-function ,arg)))
 | 
						|
         (declare (optimize (speed 3) (safety 1) (debug 1)))
 | 
						|
         (lambda (&rest arguments)
 | 
						|
           (declare (dynamic-extent arguments))
 | 
						|
           ,(compose-1 funs))))))
 | 
						|
 | 
						|
(defun multiple-value-compose (function &rest more-functions)
 | 
						|
    "Returns a function composed of FUNCTION and MORE-FUNCTIONS that applies
 | 
						|
its arguments to each in turn, starting from the rightmost of
 | 
						|
MORE-FUNCTIONS, and then calling the next one with all the return values of
 | 
						|
the last."
 | 
						|
  (declare (optimize (speed 3) (safety 1) (debug 1)))
 | 
						|
  (reduce (lambda (f g)
 | 
						|
	    (let ((f (ensure-function f))
 | 
						|
		  (g (ensure-function g)))
 | 
						|
	      (lambda (&rest arguments)
 | 
						|
		(declare (dynamic-extent arguments))
 | 
						|
		(multiple-value-call f (apply g arguments)))))
 | 
						|
          more-functions
 | 
						|
          :initial-value function))
 | 
						|
 | 
						|
(define-compiler-macro multiple-value-compose (function &rest more-functions)
 | 
						|
  (labels ((compose-1 (funs)
 | 
						|
             (if (cdr funs)
 | 
						|
                 `(multiple-value-call ,(car funs) ,(compose-1 (cdr funs)))
 | 
						|
                 `(apply ,(car funs) arguments))))
 | 
						|
    (let* ((args (cons function more-functions))
 | 
						|
           (funs (make-gensym-list (length args) "MV-COMPOSE")))
 | 
						|
      `(let ,(mapcar #'list funs args)
 | 
						|
         (declare (optimize (speed 3) (safety 1) (debug 1)))
 | 
						|
         (lambda (&rest arguments)
 | 
						|
           (declare (dynamic-extent arguments))
 | 
						|
           ,(compose-1 funs))))))
 | 
						|
 | 
						|
(declaim (inline curry rcurry))
 | 
						|
 | 
						|
(defun curry (function &rest arguments)
 | 
						|
  "Returns a function that applies ARGUMENTS and the arguments
 | 
						|
it is called with to FUNCTION."
 | 
						|
  (declare (optimize (speed 3) (safety 1)))
 | 
						|
  (let ((fn (ensure-function function)))
 | 
						|
    (lambda (&rest more)
 | 
						|
      (declare (dynamic-extent more))
 | 
						|
      ;; Using M-V-C we don't need to append the arguments.
 | 
						|
      (multiple-value-call fn (values-list arguments) (values-list more)))))
 | 
						|
 | 
						|
(define-compiler-macro curry (function &rest arguments)
 | 
						|
  (let ((curries (make-gensym-list (length arguments) "CURRY"))
 | 
						|
        (fun (gensym "FUN")))
 | 
						|
    `(let ((,fun (ensure-function ,function))
 | 
						|
           ,@(mapcar #'list curries arguments))
 | 
						|
       (declare (optimize (speed 3) (safety 1)))
 | 
						|
       (lambda (&rest more)
 | 
						|
         (declare (dynamic-extent more))
 | 
						|
         (apply ,fun ,@curries more)))))
 | 
						|
 | 
						|
(defun rcurry (function &rest arguments)
 | 
						|
  "Returns a function that applies the arguments it is called
 | 
						|
with and ARGUMENTS to FUNCTION."
 | 
						|
  (declare (optimize (speed 3) (safety 1)))
 | 
						|
  (let ((fn (ensure-function function)))
 | 
						|
    (lambda (&rest more)
 | 
						|
      (declare (dynamic-extent more))
 | 
						|
      (multiple-value-call fn (values-list more) (values-list arguments)))))
 | 
						|
 | 
						|
(define-compiler-macro rcurry (function &rest arguments)
 | 
						|
  (let ((rcurries (make-gensym-list (length arguments) "RCURRY"))
 | 
						|
        (fun (gensym "FUN")))
 | 
						|
    `(let ((,fun (ensure-function ,function))
 | 
						|
           ,@(mapcar #'list rcurries arguments))
 | 
						|
       (declare (optimize (speed 3) (safety 1)))
 | 
						|
       (lambda (&rest more)
 | 
						|
         (declare (dynamic-extent more))
 | 
						|
         (multiple-value-call ,fun (values-list more) ,@rcurries)))))
 | 
						|
 | 
						|
(declaim (notinline curry rcurry))
 | 
						|
 | 
						|
(defmacro named-lambda (name lambda-list &body body)
 | 
						|
  "Expands into a lambda-expression within whose BODY NAME denotes the
 | 
						|
corresponding function."
 | 
						|
  `(labels ((,name ,lambda-list ,@body))
 | 
						|
     #',name))
 |