@ -536,8 +536,9 @@ This function's anaphoric counterpart is `--remove-first'.
See also ` -map-first ', ` -remove-item ', and ` -remove-last '. "
See also ` -map-first ', ` -remove-item ', and ` -remove-last '. "
( --remove-first ( funcall pred it ) list ) )
( --remove-first ( funcall pred it ) list ) )
;; TODO: #'-quoting the macro upsets Emacs 24.
( defalias '-reject-first #' -remove-first )
( defalias '-reject-first #' -remove-first )
( defalias '--reject-first #' --remove-first )
( defalias '--reject-first ' --remove-first)
( defmacro --remove-last ( form list )
( defmacro --remove-last ( form list )
" Remove the last item from LIST for which FORM evals to non-nil.
" Remove the last item from LIST for which FORM evals to non-nil.
@ -1298,28 +1299,41 @@ See also: `-map-when'"
` ( -update-at , n ( lambda ( it ) ( ignore it ) , form ) , list ) )
` ( -update-at , n ( lambda ( it ) ( ignore it ) , form ) , list ) )
( defun -remove-at ( n list )
( defun -remove-at ( n list )
" Return a list with element at Nth position in LIST removed.
" Return LIST with its element at index N removed.
That is, remove any element selected as ( nth N LIST ) from LIST
and return the result.
See also: ` -remove-at-indices ', ` -remove ' "
This is a non-destructive operation: parts of LIST ( but not
necessarily all of it ) are copied as needed to avoid
destructively modifying it.
See also: ` -remove-at-indices ', ` -remove '. "
( declare ( pure t ) ( side-effect-free t ) )
( declare ( pure t ) ( side-effect-free t ) )
( -remove-at-indices ( list n ) list ) )
( if ( zerop n )
( cdr list )
( --remove-first ( = it-index n ) list ) ) )
( defun -remove-at-indices ( indices list )
( defun -remove-at-indices ( indices list )
" Return a list whose elements are elements from LIST without
" Return LIST with its elements at INDICES removed.
elements selected as ` ( nth i list ) ` for all i
That is, for each index I in INDICES, remove any element selected
from INDICES .
as ( nth I LIST ) from LIST .
See also: ` -remove-at ', ` -remove ' "
This is a non-destructive operation: parts of LIST ( but not
necessarily all of it ) are copied as needed to avoid
destructively modifying it.
See also: ` -remove-at ', ` -remove '. "
( declare ( pure t ) ( side-effect-free t ) )
( declare ( pure t ) ( side-effect-free t ) )
( let* ( ( indices ( -sort '< indices ) )
( setq indices ( --drop-while ( < it 0 ) ( -sort #' < indices ) ) )
( diffs ( cons ( car indices ) ( -map '1- ( -zip-with '- ( cdr indices ) indices ) ) ) )
( let ( ( i ( pop indices ) ) res )
r )
( --each-while list i
( --each diffs
( pop list )
( let ( ( split ( -split-at it list ) ) )
( if ( /= it-index i )
( !cons ( car split ) r )
( push it res )
( setq list ( cdr ( cadr split ) ) ) ) )
( while ( and indices ( = ( car indices ) i ) )
( !cons list r )
( pop indices ) )
( apply #' -concat ( nreverse r ) ) ) )
( setq i ( pop indices ) ) ) )
( nconc ( nreverse res ) list ) ) )
( defmacro --split-with ( pred list )
( defmacro --split-with ( pred list )
" Anaphoric form of `-split-with' . "
" Anaphoric form of `-split-with' . "
@ -1603,115 +1617,192 @@ elements of LIST. Keys are compared by `equal'."
( nreverse result ) ) ) )
( nreverse result ) ) ) )
( defmacro --zip-with ( form list1 list2 )
( defmacro --zip-with ( form list1 list2 )
" Anaphoric form of `-zip-with' .
" Zip LIST1 and LIST2 into a new list according to FORM.
That is, evaluate FORM for each item pair from the two lists, and
return the list of results. The result is as long as the shorter
list.
Each element of LIST1 and each element of LIST2 in turn are bound
pairwise to ` it ' and ` other ', respectively, and their index
within the list to ` it-index ', before evaluating FORM.
Each element in turn of LIST1 is bound to ` it ', and of LIST2 to
This is the anaphoric counterpart to ` -zip-with '. "
` other ', before evaluating FORM. "
( declare ( debug ( form form form ) ) )
( declare ( debug ( form form form ) ) )
( let ( ( r ( make-symbol " result " ) )
( let ( ( r ( make-symbol " result " ) )
( l1 ( make-symbol " list1 " ) )
( l2 ( make-symbol " list2 " ) ) )
( l2 ( make-symbol " list2 " ) ) )
` ( let ( ( , r nil )
` ( let ( ( , l2 , list2 ) , r )
( , l1 , list1 )
( --each-while , list1 , l2
( , l2 , list2 ) )
( let ( ( other ( pop , l2 ) ) )
( while ( and , l1 , l2 )
( ignore other )
( let ( ( it ( car , l1 ) )
( push , form , r ) ) )
( other ( car , l2 ) ) )
( !cons , form , r )
( !cdr , l1 )
( !cdr , l2 ) ) )
( nreverse , r ) ) ) )
( nreverse , r ) ) ) )
( defun -zip-with ( fn list1 list2 )
( defun -zip-with ( fn list1 list2 )
" Zip the two lists LIST1 and LIST2 using a function FN. This
" Zip LIST1 and LIST2 into a new list using the function FN.
function is applied pairwise taking as first argument element of
That is, apply FN pairwise taking as first argument the next
LIST1 and as second argument element of LIST2 at corresponding
element of LIST1 and as second argument the next element of LIST2
position.
at the corresponding position. The result is as long as the
shorter list.
The anaphoric form ` --zip-with ' binds the elements from LIST1 as symbol ` it ',
This function 's anaphoric counterpart is ` --zip-with '.
and the elements from LIST2 as symbol ` other '. "
For other zips, see also ` -zip-lists ' and ` -zip-fill '. "
( --zip-with ( funcall fn it other ) list1 list2 ) )
( --zip-with ( funcall fn it other ) list1 list2 ) )
( defun -zip-lists ( &rest lists )
( defun -zip-lists ( &rest lists )
" Zip LISTS together. Group the head of each list, followed by the
" Zip LISTS together.
second elements of each list, and so on. The lengths of the returned
groupings are equal to the length of the shortest input list.
Group the head of each list, followed by the second element of
each list, and so on. The number of returned groupings is equal
to the length of the shortest input list, and the length of each
grouping is equal to the number of input LISTS.
The return value is always list of lists, which is a difference
The return value is always a list of proper lists, in contrast to
from ` -zip-pair ' which returns a cons-cell in case two input
` -zip ' which returns a list of dotted pairs when only two input
lists are provided.
LISTS are provided.
See also: ` -zip ' "
See also: ` -zip-pair '. "
( declare ( pure t ) ( side-effect-free t ) )
( declare ( pure t ) ( side-effect-free t ) )
( when lists
( when lists
( let ( results )
( let ( results )
( while ( -none? 'null lists )
( while ( --every it lists )
( setq results ( cons ( mapcar ' car lists ) results ) )
( push ( mapcar #' car lists ) results )
( setq lists ( mapcar ' cdr lists ) ) )
( setq lists ( mapcar #' cdr lists ) ) )
( nreverse results ) ) ) )
( nreverse results ) ) ) )
( defun -zip-lists-fill ( fill-value &rest lists )
" Zip LISTS together, padding shorter lists with FILL-VALUE.
This is like ` -zip-lists ' ( which see ) , except it retains all
elements at positions beyond the end of the shortest list. The
number of returned groupings is equal to the length of the
longest input list, and the length of each grouping is equal to
the number of input LISTS. "
( declare ( pure t ) ( side-effect-free t ) )
( when lists
( let ( results )
( while ( --some it lists )
( push ( --map ( if it ( car it ) fill-value ) lists ) results )
( setq lists ( mapcar #' cdr lists ) ) )
( nreverse results ) ) ) )
( defun -unzip-lists ( lists )
" Unzip LISTS.
This works just like ` -zip-lists ' ( which see ) , but takes a list
of lists instead of a variable number of arguments, such that
( -unzip-lists ( -zip-lists ARGS... ) )
is identity ( given that the lists comprising ARGS are of the same
length ) . "
( declare ( pure t ) ( side-effect-free t ) )
( apply #' -zip-lists lists ) )
( defalias 'dash--length=
( if ( fboundp 'length= )
#' length=
( lambda ( list length )
( cond ( ( < length 0 ) nil )
( ( zerop length ) ( null list ) )
( ( let ( ( last ( nthcdr ( 1- length ) list ) ) )
( and last ( null ( cdr last ) ) ) ) ) ) ) )
" Return non-nil if LIST is of LENGTH.
This is a compatibility shim for ` length= ' in Emacs 28.
\n ( fn LIST LENGTH ) " )
( defun dash--zip-lists-or-pair ( _form &rest lists )
" Return a form equivalent to applying `-zip' to LISTS.
This ` compiler-macro ' warns about discouraged ` -zip ' usage and
delegates to ` -zip-lists ' or ` -zip-pair ' depending on the number
of LISTS. "
( if ( not ( dash--length= lists 2 ) )
( cons #' -zip-lists lists )
( let ( ( pair ( cons #' -zip-pair lists ) )
( msg " Use -zip-pair instead of -zip to get a list of pairs " ) )
( if ( fboundp 'macroexp-warn-and-return )
( macroexp-warn-and-return msg pair )
( message msg )
pair ) ) ) )
( defun -zip ( &rest lists )
( defun -zip ( &rest lists )
" Zip LISTS together. Group the head of each list, followed by the
" Zip LISTS together.
second elements of each list, and so on. The lengths of the returned
groupings are equal to the length of the shortest input list.
Group the head of each list, followed by the second element of
each list, and so on. The number of returned groupings is equal
If two lists are provided as arguments, return the groupings as a list
to the length of the shortest input list, and the number of items
of cons cells. Otherwise, return the groupings as a list of lists.
in each grouping is equal to the number of input LISTS.
Use ` -zip-lists ' if you need the return value to always be a list
If only two LISTS are provided as arguments, return the groupings
of lists.
as a list of dotted pairs. Otherwise, return the groupings as a
list of proper lists.
Alias: ` -zip-pair '
Since the return value changes form depending on the number of
See also: ` -zip-lists ' "
arguments, it is generally recommended to use ` -zip-lists '
( declare ( pure t ) ( side-effect-free t )
instead, or ` -zip-pair ' if a list of dotted pairs is desired.
( compiler-macro
( lambda ( form )
See also: ` -unzip '. "
( if ( not ( = 2 ( length lists ) ) )
( declare ( compiler-macro dash--zip-lists-or-pair )
( cons #' -zip-lists lists )
( pure t ) ( side-effect-free t ) )
( let ( ( msg " Use -zip-pair to get a list of pairs " ) )
;; For backward compatibility, return a list of dotted pairs if two
( if ( fboundp 'macroexp-warn-and-return )
;; arguments were provided.
( macroexp-warn-and-return
( apply ( if ( dash--length= lists 2 ) #' -zip-pair #' -zip-lists ) lists ) )
msg ( cons #' -zip-pair lists ) )
( message msg )
( defun -zip-pair ( &rest lists )
form ) ) ) ) ) )
;; To support backward compatibility, return
;; cons cells if two lists were provided.
( apply ( if ( = ( length lists ) 2 ) #' -zip-pair #' -zip-lists ) lists ) )
( defun -zip-pair ( list1 list2 )
" Zip LIST1 and LIST2 together.
" Zip LIST1 and LIST2 together.
Make a pair of the head of each list, followed by the
second elements of each list, and so on. The returned
groupings are equal to the length of the shortest input list.
See also: ` -zip-lists ' "
Make a pair with the head of each list, followed by a pair with
( --map ( cons ( car it ) ( cadr it ) ) ( -zip-lists list1 list2 ) ) )
the second element of each list, and so on. The number of pairs
returned is equal to the length of the shorter input list.
See also: ` -zip-lists '. "
( declare ( advertised-calling-convention ( list1 list2 ) " 2.20.0 " )
( pure t ) ( side-effect-free t ) )
( if ( dash--length= lists 2 )
( --zip-with ( cons it other ) ( car lists ) ( cadr lists ) )
( apply #' -zip-lists lists ) ) )
( defun -zip-fill ( fill-value &rest lists )
( defun -zip-fill ( fill-value &rest lists )
" Zip LISTS, with FILL-VALUE padded onto the shorter lists. The
" Zip LISTS together, padding shorter lists with FILL-VALUE.
lengths of the returned groupings are equal to the length of the
This is like ` -zip ' ( which see ) , except it retains all elements
longest input list. "
at positions beyond the end of the shortest list. The number of
returned groupings is equal to the length of the longest input
list, and the length of each grouping is equal to the number of
input LISTS.
Since the return value changes form depending on the number of
arguments, it is generally recommended to use ` -zip-lists-fill '
instead, unless a list of dotted pairs is explicitly desired. "
( declare ( pure t ) ( side-effect-free t ) )
( declare ( pure t ) ( side-effect-free t ) )
( apply #' -zip ( apply #' -pad ( cons fill-value lists ) ) ) )
( cond ( ( null lists ) ( ) )
( ( dash--length= lists 2 )
( let ( ( list1 ( car lists ) )
( list2 ( cadr lists ) )
results )
( while ( or list1 list2 )
( push ( cons ( if list1 ( pop list1 ) fill-value )
( if list2 ( pop list2 ) fill-value ) )
results ) )
( nreverse results ) ) )
( ( apply #' -zip-lists-fill fill-value lists ) ) ) )
( defun -unzip ( lists )
( defun -unzip ( lists )
" Unzip LISTS.
" Unzip LISTS.
This works just like ` -zip ' but takes a list of lists instead of
This works just like ` -zip ' ( which see ) , but takes a list of
a variable number of arguments, such that
lists instead of a variable number of arguments, such that
( -unzip ( -zip L1 L2 L3 . . . ) )
( -unzip ( -zip L1 L2 L3 . . . ) )
is identity ( given that the lists are the same length ) .
is identity ( given that the lists are of the same length, and
that ` -zip ' is not called with two arguments, because of the
caveat described in its docstring ) .
Note in particular that calling this on a list of two lists will
Note in particular that calling ` -unzip ' on a list of two lists
return a list of cons-cells such that the above identity work s.
will return a list of dotted pair s.
See also: ` -zip ' "
Since the return value changes form depending on the number of
;; FIXME: Huh? AFAIK `-zip' is not its own inverse.
LISTS, it is generally recommended to use ` -unzip-lists ' instead. "
;; (-unzip (-zip '(1 2 3) '(a b c)))
( declare ( pure t ) ( side-effect-free t ) )
;; gives me "Wrong type argument: listp, a"
( apply #' -zip lists ) )
( apply #' -zip lists ) )
( defun -cycle ( list )
( defun -cycle ( list )
@ -2228,7 +2319,7 @@ This method normalizes PATTERN to the format expected by
( let ( ( normalized ( list ( car pattern ) ) )
( let ( ( normalized ( list ( car pattern ) ) )
( skip nil )
( skip nil )
( fill-placeholder ( make-symbol " --dash-fill-placeholder-- " ) ) )
( fill-placeholder ( make-symbol " --dash-fill-placeholder-- " ) ) )
( -each ( apply #' -zip ( -pad fill-placeholder ( cdr pattern ) ( cddr pattern ) ) )
( -each ( -zip-fill fill-placeholder ( cdr pattern ) ( cddr pattern ) )
( lambda ( pair )
( lambda ( pair )
( let ( ( current ( car pair ) )
( let ( ( current ( car pair ) )
( next ( cdr pair ) ) )
( next ( cdr pair ) ) )
@ -2567,7 +2658,8 @@ because we need to support improper list binding."
,@ body )
,@ body )
( let* ( ( varlist ( dash--normalize-let-varlist varlist ) )
( let* ( ( varlist ( dash--normalize-let-varlist varlist ) )
( inputs ( --map-indexed ( list ( make-symbol ( format " input%d " it-index ) ) ( cadr it ) ) varlist ) )
( inputs ( --map-indexed ( list ( make-symbol ( format " input%d " it-index ) ) ( cadr it ) ) varlist ) )
( new-varlist ( --map ( list ( caar it ) ( cadr it ) ) ( -zip-pair varlist inputs ) ) ) )
( new-varlist ( --zip-with ( list ( car it ) ( car other ) )
varlist inputs ) ) )
` ( let , inputs
` ( let , inputs
( -let* , new-varlist ,@ body ) ) ) ) )
( -let* , new-varlist ,@ body ) ) ) ) )
@ -3742,7 +3834,8 @@ This function satisfies the following laws:
( -compose ( -partial # \\= 'nth n )
( -compose ( -partial # \\= 'nth n )
( -prod f1 f2 . . . ) )
( -prod f1 f2 . . . ) )
= ( -compose fn ( -partial # \\= 'nth n ) ) "
= ( -compose fn ( -partial # \\= 'nth n ) ) "
( lambda ( x ) ( -zip-with 'funcall fns x ) ) )
( declare ( pure t ) ( side-effect-free t ) )
( lambda ( x ) ( --zip-with ( funcall it other ) fns x ) ) )
;;; Font lock
;;; Font lock
@ -3868,7 +3961,8 @@ which do not dynamically detect macros, Dash-Fontify mode
additionally fontifies Dash macro calls.
additionally fontifies Dash macro calls.
See also ` dash-fontify-mode-lighter ' and
See also ` dash-fontify-mode-lighter ' and
` global-dash-fontify-mode '. " :lighter dash-fontify-mode-lighter
` global-dash-fontify-mode '. "
:lighter dash-fontify-mode-lighter
( if dash-fontify-mode
( if dash-fontify-mode
( font-lock-add-keywords nil dash--keywords t )
( font-lock-add-keywords nil dash--keywords t )
( font-lock-remove-keywords nil dash--keywords ) )
( font-lock-remove-keywords nil dash--keywords ) )