branch: externals/dash commit 5326ed8c9f1787d6dc08cf3594f42bcd58cebb24 Author: Basil L. Contovounesios <conto...@tcd.ie> Commit: Basil L. Contovounesios <conto...@tcd.ie>
Improve -rpartial, -juxt, and -compose * dash.el (-rpartial, -juxt): Mark as pure and side-effect-free. Fix docstrings. (-compose): Ditto. Optimize for speed. * dev/examples.el (-rpartial, -juxt, -compose): Extend tests. * README.md: * dash.texi: Regenerate docs. --- README.md | 44 +++++++++++++++++++++++------------------- dash.el | 42 +++++++++++++++++++++++++--------------- dash.texi | 60 +++++++++++++++++++++++++++++++++------------------------ dev/examples.el | 45 +++++++++++++++++++++++++++++-------------- 4 files changed, 116 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index 4947b6d..a52493c 100644 --- a/README.md +++ b/README.md @@ -2811,40 +2811,44 @@ was called. #### -rpartial `(fn &rest args)` -Takes a function `fn` and fewer than the normal arguments to `fn`, -and returns a fn that takes a variable number of additional `args`. -When called, the returned function calls `fn` with the additional -args first and then `args`. +Return a function that is a partial application of `fn` to `args`. +`args` is a list of the last `n` arguments to pass to `fn`. The result +is a new function which does the same as `fn`, except that the last +`n` arguments are fixed at the values with which this function was +called. This is like [`-partial`](#-partial-fun-rest-args), except the arguments are fixed +starting from the right rather than the left. ```el -(funcall (-rpartial '- 5) 8) ;; => 3 -(funcall (-rpartial '- 5 2) 10) ;; => 3 +(funcall (-rpartial #'- 5)) ;; => -5 +(funcall (-rpartial #'- 5) 8) ;; => 3 +(funcall (-rpartial #'- 5 2) 10) ;; => 3 ``` #### -juxt `(&rest fns)` -Takes a list of functions and returns a fn that is the -juxtaposition of those fns. The returned fn takes a variable -number of args, and returns a list containing the result of -applying each fn to the args (left-to-right). +Return a function that is the juxtaposition of `fns`. +The returned function takes a variable number of `args`, applies +each of `fns` in turn to `args`, and returns the list of results. ```el -(funcall (-juxt '+ '-) 3 5) ;; => (8 -2) -(-map (-juxt 'identity 'square) '(1 2 3)) ;; => ((1 1) (2 4) (3 9)) +(funcall (-juxt) 1 2) ;; => () +(funcall (-juxt #'+ #'- #'* #'/) 7 5) ;; => (12 2 35 1) +(mapcar (-juxt #'number-to-string #'1+) '(1 2)) ;; => (("1" 2) ("2" 3)) ``` #### -compose `(&rest fns)` -Takes a list of functions and returns a fn that is the -composition of those fns. The returned fn takes a variable -number of arguments, and returns the result of applying -each fn to the result of applying the previous fn to -the arguments (right-to-left). +Compose `fns` into a single composite function. +Return a function that takes a variable number of `args`, applies +the last function in `fns` to `args`, and returns the result of +calling each remaining function on the result of the previous +function, right-to-left. If no `fns` are given, return a variadic +`identity` function. ```el -(funcall (-compose 'square '+) 2 3) ;; => (square (+ 2 3)) -(funcall (-compose 'identity 'square) 3) ;; => (square 3) -(funcall (-compose 'square 'identity) 3) ;; => (square 3) +(funcall (-compose #'- #'1+ #'+) 1 2 3) ;; => -7 +(funcall (-compose #'identity #'1+) 3) ;; => 4 +(mapcar (-compose #'not #'stringp) '(nil "")) ;; => (t nil) ``` #### -applify `(fn)` diff --git a/dash.el b/dash.el index ffcf55f..499a883 100644 --- a/dash.el +++ b/dash.el @@ -3002,28 +3002,38 @@ structure such as plist or alist." (defalias '-partial #'apply-partially) (defun -rpartial (fn &rest args) - "Takes a function FN and fewer than the normal arguments to FN, -and returns a fn that takes a variable number of additional ARGS. -When called, the returned function calls FN with the additional -args first and then ARGS." + "Return a function that is a partial application of FN to ARGS. +ARGS is a list of the last N arguments to pass to FN. The result +is a new function which does the same as FN, except that the last +N arguments are fixed at the values with which this function was +called. This is like `-partial', except the arguments are fixed +starting from the right rather than the left." + (declare (pure t) (side-effect-free t)) (lambda (&rest args-before) (apply fn (append args-before args)))) (defun -juxt (&rest fns) - "Takes a list of functions and returns a fn that is the -juxtaposition of those fns. The returned fn takes a variable -number of args, and returns a list containing the result of -applying each fn to the args (left-to-right)." + "Return a function that is the juxtaposition of FNS. +The returned function takes a variable number of ARGS, applies +each of FNS in turn to ARGS, and returns the list of results." + (declare (pure t) (side-effect-free t)) (lambda (&rest args) (mapcar (lambda (x) (apply x args)) fns))) (defun -compose (&rest fns) - "Takes a list of functions and returns a fn that is the -composition of those fns. The returned fn takes a variable -number of arguments, and returns the result of applying -each fn to the result of applying the previous fn to -the arguments (right-to-left)." - (lambda (&rest args) - (car (-reduce-r-from (lambda (fn xs) (list (apply fn xs))) - args fns)))) + "Compose FNS into a single composite function. +Return a function that takes a variable number of ARGS, applies +the last function in FNS to ARGS, and returns the result of +calling each remaining function on the result of the previous +function, right-to-left. If no FNS are given, return a variadic +`identity' function." + (declare (pure t) (side-effect-free t)) + (let* ((fns (nreverse fns)) + (head (car fns)) + (tail (cdr fns))) + (cond (tail + (lambda (&rest args) + (--reduce-from (funcall it acc) (apply head args) tail))) + (fns head) + ((lambda (&optional arg &rest _) arg))))) (defun -applify (fn) "Return a function that applies FN to a single list of args. diff --git a/dash.texi b/dash.texi index dcfbe0d..60aabb4 100644 --- a/dash.texi +++ b/dash.texi @@ -4226,18 +4226,24 @@ was called. @anchor{-rpartial} @defun -rpartial (fn &rest args) -Takes a function @var{fn} and fewer than the normal arguments to @var{fn}, -and returns a fn that takes a variable number of additional @var{args}. -When called, the returned function calls @var{fn} with the additional -args first and then @var{args}. +Return a function that is a partial application of @var{fn} to @var{args}. +@var{args} is a list of the last @var{n} arguments to pass to @var{fn}. The result +is a new function which does the same as @var{fn}, except that the last +@var{n} arguments are fixed at the values with which this function was +called. This is like @code{-partial} (@pxref{-partial}), except the arguments are fixed +starting from the right rather than the left. @example @group -(funcall (-rpartial '- 5) 8) +(funcall (-rpartial #'- 5)) + @result{} -5 +@end group +@group +(funcall (-rpartial #'- 5) 8) @result{} 3 @end group @group -(funcall (-rpartial '- 5 2) 10) +(funcall (-rpartial #'- 5 2) 10) @result{} 3 @end group @end example @@ -4245,43 +4251,47 @@ args first and then @var{args}. @anchor{-juxt} @defun -juxt (&rest fns) -Takes a list of functions and returns a fn that is the -juxtaposition of those fns. The returned fn takes a variable -number of args, and returns a list containing the result of -applying each fn to the args (left-to-right). +Return a function that is the juxtaposition of @var{fns}. +The returned function takes a variable number of @var{args}, applies +each of @var{fns} in turn to @var{args}, and returns the list of results. @example @group -(funcall (-juxt '+ '-) 3 5) - @result{} (8 -2) +(funcall (-juxt) 1 2) + @result{} () +@end group +@group +(funcall (-juxt #'+ #'- #'* #'/) 7 5) + @result{} (12 2 35 1) @end group @group -(-map (-juxt 'identity 'square) '(1 2 3)) - @result{} ((1 1) (2 4) (3 9)) +(mapcar (-juxt #'number-to-string #'1+) '(1 2)) + @result{} (("1" 2) ("2" 3)) @end group @end example @end defun @anchor{-compose} @defun -compose (&rest fns) -Takes a list of functions and returns a fn that is the -composition of those fns. The returned fn takes a variable -number of arguments, and returns the result of applying -each fn to the result of applying the previous fn to -the arguments (right-to-left). +Compose @var{fns} into a single composite function. +Return a function that takes a variable number of @var{args}, applies +the last function in @var{fns} to @var{args}, and returns the result of +calling each remaining function on the result of the previous +function, right-to-left. If no @var{fns} are given, return a variadic +@code{identity} function. @example @group -(funcall (-compose 'square '+) 2 3) - @result{} (square (+ 2 3)) +(funcall (-compose #'- #'1+ #'+) 1 2 3) + @result{} -7 @end group @group -(funcall (-compose 'identity 'square) 3) - @result{} (square 3) +(funcall (-compose #'identity #'1+) 3) + @result{} 4 @end group @group -(funcall (-compose 'square 'identity) 3) - @result{} (square 3) +(mapcar (-compose #'not #'stringp) '(nil "")) + @result{} (t nil) @end group @end example @end defun diff --git a/dev/examples.el b/dev/examples.el index 35acfd4..e281702 100644 --- a/dev/examples.el +++ b/dev/examples.el @@ -1640,20 +1640,37 @@ or readability." (funcall (-partial #'+) 5) => 5 (apply (-partial #'+ 5) 10 '(1 2)) => 18) - (unless (version< emacs-version "24") - (defexamples -rpartial - (funcall (-rpartial '- 5) 8) => 3 - (funcall (-rpartial '- 5 2) 10) => 3) - - (defexamples -juxt - (funcall (-juxt '+ '-) 3 5) => '(8 -2) - (-map (-juxt 'identity 'square) '(1 2 3)) => '((1 1) (2 4) (3 9))) - - (defexamples -compose - (funcall (-compose 'square '+) 2 3) => (square (+ 2 3)) - (funcall (-compose 'identity 'square) 3) => (square 3) - (funcall (-compose 'square 'identity) 3) => (square 3) - (funcall (-compose (-compose 'not 'even?) 'square) 3) => (funcall (-compose 'not (-compose 'even? 'square)) 3))) + (defexamples -rpartial + (funcall (-rpartial #'- 5)) => -5 + (funcall (-rpartial #'- 5) 8) => 3 + (funcall (-rpartial #'- 5 2) 10) => 3 + (funcall (-rpartial #'-)) => 0 + (apply (-rpartial #'- 1) 2 '(20 3)) => -22) + + (defexamples -juxt + (funcall (-juxt) 1 2) => '() + (funcall (-juxt #'+ #'- #'* #'/) 7 5) => '(12 2 35 1) + (mapcar (-juxt #'number-to-string #'1+) '(1 2)) => '(("1" 2) ("2" 3)) + (funcall (-juxt #'+ #'-)) => '(0 0) + (funcall (-juxt)) => '()) + + (defexamples -compose + (funcall (-compose #'- #'1+ #'+) 1 2 3) => -7 + (funcall (-compose #'identity #'1+) 3) => 4 + (mapcar (-compose #'not #'stringp) '(nil "")) => '(t nil) + (funcall (-compose #'1+ #'identity) 3) => 4 + (mapcar (lambda (fn) + (list (funcall fn 0) (funcall fn 1))) + (list (-compose (-compose #'natnump #'1+) #'lognot) + (-compose #'natnump (-compose #'1+ #'lognot)) + (-compose #'natnump #'1+ #'lognot))) + => '((t nil) (t nil) (t nil)) + (funcall (-compose)) => nil + (funcall (-compose) nil) => nil + (funcall (-compose) nil 1) => nil + (funcall (-compose) 1) => 1 + (funcall (-compose) 1 2) => 1 + (-compose #'+) => #'+) (defexamples -applify (funcall (-applify #'+) ()) => 0