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.