siiky
2022/01/09
2022/07/07
en
I've been using my own pipe operators for a while. This is the latest definition:
(define (*=> vals . funs) (foldl (lambda (val fun) (fun val)) (apply (car funs) vals) (cdr funs))) (define ((*-> . funs) . vals) (foldl (lambda (val fun) (fun val)) (apply (car funs) vals) (cdr funs))) (define (=> val . funs) (foldl (lambda (val fun) (fun val)) val funs)) (define ((-> . funs) val) (foldl (lambda (val fun) (fun val)) val funs))
And this is the original definition (with slightly different semantics):
(define (=*> val funs) (foldl (lambda (val fun) (fun val)) val funs)) (define ((-*> funs) val) (=*> val funs)) (define (=> val . funs) (=*> val funs)) (define ((-> . funs) val) (=*> val funs))
Comparing with Scheme's `o`:
(o snd fst) (-> fst snd)
There's no equivalent to Scheme's `compose`.
I've never used =*> and -*> directly (they're there just in case, and as the base for the other two), but I've grown attached to -> and =>.
The reasoning behind the names is simple: think of a function as a "processing pipe". -> is a chain of such pipes, and a pipe on its own, without "contents" -- you have to plug something on one end to get something on the other end. On the other hand, => already has the stuff plugged in, ready to go, so it's fatter.
Some uses:
(map (-> do-this and-that) some-list) (=> some-list (cute map (-> do-this and-that) <>) (cute filter (o not screwed?) <>)) ((-> (cute map (-> do-this and-that) <>) (cute filter (o not screwed?) <>)) some-list) (filter (o not screwed?) (map (-> do-this and-that) some-list))
(-> do-this and-that) is an unary function -- that's why it can be given to map.
(=> some-list ...) evaluates to a value, which is the result of applying the filter to the result of applying the map to some-list.
The third and fourth expressions, ((-> ...) some-list) and (filter ...), are equivalent to the second.
Note the use of o instead of -> in the filter's predicate. Personal preference, but I think that case reads better with o because it's more like English.
-----
But now that'll probably be the end of them for me.
Yesterday I learned of SRFI-197 -- very cool! There's even an egg for CHICKEN already.
And I can rename the exported identifiers to the ones I've been using:
(import chicken.module (rename (only srfi-197 chain chain-lambda) (chain =>) (chain-lambda ->)))
With that, the previous example is written like so:
(map (-> (do-this _) (and-that _)) some-list) (=> some-list (map (-> (do-this _) (and-that _)) _) (filter (o not screwed?) _)) ((-> (map (-> (do-this _) (and-that _)) _) (filter (o not screwed?) _)) some-list) (filter (o not screwed?) (map (-> (do-this _) (and-that _)) some-list))
Maybe the advantage(s) aren't obvious (maybe they're not advantages at all!),
but to me not having to write `cute` for non-unary functions is a plus, even if
I'm now forced to write parenthesis and an underscore on every unary function.
I guess the only situation(s) I don't see myself using it is if I want to avoid
dependencies.