Skip to content

Commit 350b4c3

Browse files
committed
Merge PR #308 from basil-conto/blc/variadic
Closes: #72, #306.
2 parents 774aa96 + 11a6296 commit 350b4c3

File tree

5 files changed

+399
-141
lines changed

5 files changed

+399
-141
lines changed

NEWS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ See the end of the file for license conditions.
1818

1919
#### New features
2020

21+
- The combinators `-on`, `-flip`, `-not`, `-andfn`, and `-orfn` now
22+
return variadic functions that take any number of arguments (#308).
23+
- New combinator `-rotate-args` similar to `-flip`, but for arbitrary
24+
arglist rotations (suggested by @vapniks, #72).
2125
- New function `-every` and its anaphoric macro counterpart `--every`.
2226
They are like the existing `-every-p` and `--every-p`, respectively,
2327
but return the last non-`nil` result instead of just `t`.

README.md

Lines changed: 75 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -373,8 +373,9 @@ Functions that manipulate and compose other functions.
373373
* [`-juxt`](#-juxt-rest-fns) `(&rest fns)`
374374
* [`-compose`](#-compose-rest-fns) `(&rest fns)`
375375
* [`-applify`](#-applify-fn) `(fn)`
376-
* [`-on`](#-on-operator-transformer) `(operator transformer)`
377-
* [`-flip`](#-flip-func) `(func)`
376+
* [`-on`](#-on-op-trans) `(op trans)`
377+
* [`-flip`](#-flip-fn) `(fn)`
378+
* [`-rotate-args`](#-rotate-args-n-fn) `(n fn)`
378379
* [`-const`](#-const-c) `(c)`
379380
* [`-cut`](#-cut-rest-params) `(&rest params)`
380381
* [`-not`](#-not-pred) `(pred)`
@@ -1193,7 +1194,7 @@ Return the smallest value from `list` of numbers or markers.
11931194
Take a comparison function `comparator` and a `list` and return
11941195
the least element of the list by the comparison function.
11951196

1196-
See also combinator [`-on`](#-on-operator-transformer) which can transform the values before
1197+
See also combinator [`-on`](#-on-op-trans) which can transform the values before
11971198
comparing them.
11981199

11991200
```el
@@ -1217,7 +1218,7 @@ Return the largest value from `list` of numbers or markers.
12171218
Take a comparison function `comparator` and a `list` and return
12181219
the greatest element of the list by the comparison function.
12191220

1220-
See also combinator [`-on`](#-on-operator-transformer) which can transform the values before
1221+
See also combinator [`-on`](#-on-op-trans) which can transform the values before
12211222
comparing them.
12221223

12231224
```el
@@ -2892,30 +2893,56 @@ taking 1 argument which is a list of `n` arguments.
28922893
(funcall (-applify #'<) '(3 6)) ;; => t
28932894
```
28942895

2895-
#### -on `(operator transformer)`
2896+
#### -on `(op trans)`
28962897

2897-
Return a function of two arguments that first applies
2898-
`transformer` to each of them and then applies `operator` on the
2899-
results (in the same order).
2898+
Return a function that calls `trans` on each arg and `op` on the results.
2899+
The returned function takes a variable number of arguments, calls
2900+
the function `trans` on each one in turn, and then passes those
2901+
results as the list of arguments to `op`, in the same order.
29002902

2901-
In types: (b -> b -> c) -> (a -> b) -> a -> a -> c
2903+
For example, the following pairs of expressions are morally
2904+
equivalent:
2905+
2906+
(funcall (-on #'+ #'1+) 1 2 3) = (+ (1+ 1) (1+ 2) (1+ 3))
2907+
(funcall (-on #'+ #'1+)) = (+)
29022908

29032909
```el
2904-
(-sort (-on '< 'length) '((1 2 3) (1) (1 2))) ;; => ((1) (1 2) (1 2 3))
2905-
(-min-by (-on '> 'length) '((1 2 3) (4) (1 2))) ;; => (4)
2906-
(-min-by (-on 'string-lessp 'number-to-string) '(2 100 22)) ;; => 22
2910+
(-sort (-on #'< #'length) '((1 2 3) (1) (1 2))) ;; => ((1) (1 2) (1 2 3))
2911+
(funcall (-on #'min #'string-to-number) "22" "2" "1" "12") ;; => 1
2912+
(-min-by (-on #'> #'length) '((1 2 3) (4) (1 2))) ;; => (4)
29072913
```
29082914

2909-
#### -flip `(func)`
2915+
#### -flip `(fn)`
2916+
2917+
Return a function that calls `fn` with its arguments reversed.
2918+
The returned function takes the same number of arguments as `fn`.
2919+
2920+
For example, the following two expressions are morally
2921+
equivalent:
29102922

2911-
Swap the order of arguments for binary function `func`.
2923+
(funcall (-flip #'-) 1 2) = (- 2 1)
29122924

2913-
In types: (a -> b -> c) -> b -> a -> c
2925+
See also: [`-rotate-args`](#-rotate-args-n-fn).
29142926

29152927
```el
2916-
(funcall (-flip '<) 2 1) ;; => t
2917-
(funcall (-flip '-) 3 8) ;; => 5
2918-
(-sort (-flip '<) '(4 3 6 1)) ;; => (6 4 3 1)
2928+
(-sort (-flip #'<) '(4 3 6 1)) ;; => (6 4 3 1)
2929+
(funcall (-flip #'-) 3 2 1 10) ;; => 4
2930+
(funcall (-flip #'1+) 1) ;; => 2
2931+
```
2932+
2933+
#### -rotate-args `(n fn)`
2934+
2935+
Return a function that calls `fn` with args rotated `n` places to the right.
2936+
The returned function takes the same number of arguments as `fn`,
2937+
rotates the list of arguments `n` places to the right (left if `n` is
2938+
negative) just like [`-rotate`](#-rotate-n-list), and applies `fn` to the result.
2939+
2940+
See also: [`-flip`](#-flip-fn).
2941+
2942+
```el
2943+
(funcall (-rotate-args -1 #'list) 1 2 3 4) ;; => (2 3 4 1)
2944+
(funcall (-rotate-args 1 #'-) 1 10 100) ;; => 89
2945+
(funcall (-rotate-args 2 #'list) 3 4 5 1 2) ;; => (1 2 3 4 5)
29192946
```
29202947

29212948
#### -const `(c)`
@@ -2926,8 +2953,8 @@ In types: a -> b -> a
29262953

29272954
```el
29282955
(funcall (-const 2) 1 3 "foo") ;; => 2
2929-
(-map (-const 1) '("a" "b" "c" "d")) ;; => (1 1 1 1)
2930-
(-sum (-map (-const 1) '("a" "b" "c" "d"))) ;; => 4
2956+
(mapcar (-const 1) '("a" "b" "c" "d")) ;; => (1 1 1 1)
2957+
(-sum (mapcar (-const 1) '("a" "b" "c" "d"))) ;; => 4
29312958
```
29322959

29332960
#### -cut `(&rest params)`
@@ -2945,40 +2972,50 @@ See `srfi-26` for detailed description.
29452972

29462973
#### -not `(pred)`
29472974

2948-
Take a unary predicate `pred` and return a unary predicate
2949-
that returns t if `pred` returns nil and nil if `pred` returns
2950-
non-nil.
2975+
Return a predicate that negates the result of `pred`.
2976+
The returned predicate passes its arguments to `pred`. If `pred`
2977+
returns nil, the result is non-nil; otherwise the result is nil.
2978+
2979+
See also: [`-andfn`](#-andfn-rest-preds) and [`-orfn`](#-orfn-rest-preds).
29512980

29522981
```el
2953-
(funcall (-not 'even?) 5) ;; => t
2954-
(-filter (-not (-partial '< 4)) '(1 2 3 4 5 6 7 8)) ;; => (1 2 3 4)
2982+
(funcall (-not #'numberp) "5") ;; => t
2983+
(-sort (-not #'<) '(5 2 1 0 6)) ;; => (6 5 2 1 0)
2984+
(-filter (-not (-partial #'< 4)) '(1 2 3 4 5 6 7 8)) ;; => (1 2 3 4)
29552985
```
29562986

29572987
#### -orfn `(&rest preds)`
29582988

2959-
Take list of unary predicates `preds` and return a unary
2960-
predicate with argument x that returns non-nil if at least one of
2961-
the `preds` returns non-nil on x.
2989+
Return a predicate that returns the first non-nil result of `preds`.
2990+
The returned predicate takes a variable number of arguments,
2991+
passes them to each predicate in `preds` in turn until one of them
2992+
returns non-nil, and returns that non-nil result without calling
2993+
the remaining `preds`. If all `preds` return nil, or if no `preds` are
2994+
given, the returned predicate returns nil.
29622995

2963-
In types: [a -> Bool] -> a -> Bool
2996+
See also: [`-andfn`](#-andfn-rest-preds) and [`-not`](#-not-pred).
29642997

29652998
```el
2966-
(-filter (-orfn 'even? (-partial (-flip '<) 5)) '(1 2 3 4 5 6 7 8 9 10)) ;; => (1 2 3 4 6 8 10)
2967-
(funcall (-orfn 'stringp 'even?) "foo") ;; => t
2999+
(-filter (-orfn #'natnump #'booleanp) '(1 nil "a" -4 b c t)) ;; => (1 nil t)
3000+
(funcall (-orfn #'symbolp (-cut string-match-p "x" <>)) "axe") ;; => 1
3001+
(funcall (-orfn #'= #'+) 1 1) ;; => t
29683002
```
29693003

29703004
#### -andfn `(&rest preds)`
29713005

2972-
Take list of unary predicates `preds` and return a unary
2973-
predicate with argument x that returns non-nil if all of the
2974-
`preds` returns non-nil on x.
3006+
Return a predicate that returns non-nil if all `preds` do so.
3007+
The returned predicate `p` takes a variable number of arguments and
3008+
passes them to each predicate in `preds` in turn. If any one of
3009+
`preds` returns nil, `p` also returns nil without calling the
3010+
remaining `preds`. If all `preds` return non-nil, `p` returns the last
3011+
such value. If no `preds` are given, `p` always returns non-nil.
29753012

2976-
In types: [a -> Bool] -> a -> Bool
3013+
See also: [`-orfn`](#-orfn-rest-preds) and [`-not`](#-not-pred).
29773014

29783015
```el
2979-
(funcall (-andfn (-cut < <> 10) 'even?) 6) ;; => t
2980-
(funcall (-andfn (-cut < <> 10) 'even?) 12) ;; => nil
2981-
(-filter (-andfn (-not 'even?) (-cut >= 5 <>)) '(1 2 3 4 5 6 7 8 9 10)) ;; => (1 3 5)
3016+
(-filter (-andfn #'numberp (-cut < <> 5)) '(a 1 b 6 c 2)) ;; => (1 2)
3017+
(mapcar (-andfn #'numberp #'1+) '(a 1 b 6)) ;; => (nil 2 nil 7)
3018+
(funcall (-andfn #'= #'+) 1 1) ;; => 2
29823019
```
29833020

29843021
#### -iteratefn `(fn n)`

dash.el

Lines changed: 98 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3113,24 +3113,73 @@ taking 1 argument which is a list of N arguments."
31133113
(declare (pure t) (side-effect-free t))
31143114
(lambda (args) (apply fn args)))
31153115

3116-
(defun -on (operator transformer)
3117-
"Return a function of two arguments that first applies
3118-
TRANSFORMER to each of them and then applies OPERATOR on the
3119-
results (in the same order).
3116+
(defun -on (op trans)
3117+
"Return a function that calls TRANS on each arg and OP on the results.
3118+
The returned function takes a variable number of arguments, calls
3119+
the function TRANS on each one in turn, and then passes those
3120+
results as the list of arguments to OP, in the same order.
31203121
3121-
In types: (b -> b -> c) -> (a -> b) -> a -> a -> c"
3122-
(lambda (x y) (funcall operator (funcall transformer x) (funcall transformer y))))
3122+
For example, the following pairs of expressions are morally
3123+
equivalent:
31233124
3124-
(defun -flip (func)
3125-
"Swap the order of arguments for binary function FUNC.
3126-
3127-
In types: (a -> b -> c) -> b -> a -> c"
3128-
(lambda (x y) (funcall func y x)))
3125+
(funcall (-on #\\='+ #\\='1+) 1 2 3) = (+ (1+ 1) (1+ 2) (1+ 3))
3126+
(funcall (-on #\\='+ #\\='1+)) = (+)"
3127+
(declare (pure t) (side-effect-free t))
3128+
(lambda (&rest args)
3129+
;; This unrolling seems to be a relatively cheap way to keep the
3130+
;; overhead of `mapcar' + `apply' in check.
3131+
(cond ((cddr args)
3132+
(apply op (mapcar trans args)))
3133+
((cdr args)
3134+
(funcall op (funcall trans (car args)) (funcall trans (cadr args))))
3135+
(args
3136+
(funcall op (funcall trans (car args))))
3137+
((funcall op)))))
3138+
3139+
(defun -flip (fn)
3140+
"Return a function that calls FN with its arguments reversed.
3141+
The returned function takes the same number of arguments as FN.
3142+
3143+
For example, the following two expressions are morally
3144+
equivalent:
3145+
3146+
(funcall (-flip #\\='-) 1 2) = (- 2 1)
3147+
3148+
See also: `-rotate-args'."
3149+
(declare (pure t) (side-effect-free t))
3150+
(lambda (&rest args) ;; Open-code for speed.
3151+
(cond ((cddr args) (apply fn (nreverse args)))
3152+
((cdr args) (funcall fn (cadr args) (car args)))
3153+
(args (funcall fn (car args)))
3154+
((funcall fn)))))
3155+
3156+
(defun -rotate-args (n fn)
3157+
"Return a function that calls FN with args rotated N places to the right.
3158+
The returned function takes the same number of arguments as FN,
3159+
rotates the list of arguments N places to the right (left if N is
3160+
negative) just like `-rotate', and applies FN to the result.
3161+
3162+
See also: `-flip'."
3163+
(declare (pure t) (side-effect-free t))
3164+
(if (zerop n)
3165+
fn
3166+
(let ((even (= (% n 2) 0)))
3167+
(lambda (&rest args)
3168+
(cond ((cddr args) ;; Open-code for speed.
3169+
(apply fn (-rotate n args)))
3170+
((cdr args)
3171+
(let ((fst (car args))
3172+
(snd (cadr args)))
3173+
(funcall fn (if even fst snd) (if even snd fst))))
3174+
(args
3175+
(funcall fn (car args)))
3176+
((funcall fn)))))))
31293177

31303178
(defun -const (c)
31313179
"Return a function that returns C ignoring any additional arguments.
31323180
31333181
In types: a -> b -> a"
3182+
(declare (pure t) (side-effect-free t))
31343183
(lambda (&rest _) c))
31353184

31363185
(defmacro -cut (&rest params)
@@ -3147,30 +3196,51 @@ See SRFI-26 for detailed description."
31473196
`(lambda ,args
31483197
,(let ((body (--map (if (eq it '<>) (pop args) it) params)))
31493198
(if (eq (car params) '<>)
3150-
(cons 'funcall body)
3199+
(cons #'funcall body)
31513200
body)))))
31523201

31533202
(defun -not (pred)
3154-
"Take a unary predicate PRED and return a unary predicate
3155-
that returns t if PRED returns nil and nil if PRED returns
3156-
non-nil."
3157-
(lambda (x) (not (funcall pred x))))
3203+
"Return a predicate that negates the result of PRED.
3204+
The returned predicate passes its arguments to PRED. If PRED
3205+
returns nil, the result is non-nil; otherwise the result is nil.
31583206
3159-
(defun -orfn (&rest preds)
3160-
"Take list of unary predicates PREDS and return a unary
3161-
predicate with argument x that returns non-nil if at least one of
3162-
the PREDS returns non-nil on x.
3207+
See also: `-andfn' and `-orfn'."
3208+
(declare (pure t) (side-effect-free t))
3209+
(lambda (&rest args) (not (apply pred args))))
31633210

3164-
In types: [a -> Bool] -> a -> Bool"
3165-
(lambda (x) (-any? (-cut funcall <> x) preds)))
3211+
(defun -orfn (&rest preds)
3212+
"Return a predicate that returns the first non-nil result of PREDS.
3213+
The returned predicate takes a variable number of arguments,
3214+
passes them to each predicate in PREDS in turn until one of them
3215+
returns non-nil, and returns that non-nil result without calling
3216+
the remaining PREDS. If all PREDS return nil, or if no PREDS are
3217+
given, the returned predicate returns nil.
3218+
3219+
See also: `-andfn' and `-not'."
3220+
(declare (pure t) (side-effect-free t))
3221+
;; Open-code for speed.
3222+
(cond ((cdr preds) (lambda (&rest args) (--some (apply it args) preds)))
3223+
(preds (car preds))
3224+
(#'ignore)))
31663225

31673226
(defun -andfn (&rest preds)
3168-
"Take list of unary predicates PREDS and return a unary
3169-
predicate with argument x that returns non-nil if all of the
3170-
PREDS returns non-nil on x.
3171-
3172-
In types: [a -> Bool] -> a -> Bool"
3173-
(lambda (x) (-all? (-cut funcall <> x) preds)))
3227+
"Return a predicate that returns non-nil if all PREDS do so.
3228+
The returned predicate P takes a variable number of arguments and
3229+
passes them to each predicate in PREDS in turn. If any one of
3230+
PREDS returns nil, P also returns nil without calling the
3231+
remaining PREDS. If all PREDS return non-nil, P returns the last
3232+
such value. If no PREDS are given, P always returns non-nil.
3233+
3234+
See also: `-orfn' and `-not'."
3235+
(declare (pure t) (side-effect-free t))
3236+
;; Open-code for speed.
3237+
(cond ((cdr preds) (lambda (&rest args) (--every (apply it args) preds)))
3238+
(preds (car preds))
3239+
;; As a `pure' function, this runtime check may generate
3240+
;; backward-incompatible bytecode for `(-andfn)' at compile-time,
3241+
;; but I doubt that's a problem in practice (famous last words).
3242+
((fboundp 'always) #'always)
3243+
((lambda (&rest _) t))))
31743244

31753245
(defun -iteratefn (fn n)
31763246
"Return a function FN composed N times with itself.

0 commit comments

Comments
 (0)