I'm not an expert on functional style programming and don't intend, right now, to explain or justify its merits, except to say that sometimes it produces cleaner, clearer code. But while I have a current preference for Common Lisp above most other (programming) languages at the present, I really like that forward pipe operator in F#. This morning, I thought it was time to cross that bridge—try to make an imitation forward pipe in Common Lisp.
In F#, if you wanted to turn a into b into c, you might do something like
let a2c x =
a2b x
|> b2c
where b2c also takes one parameter. In Lisp, you end up with something more like
let a2c x =
a2b x
|> b2c
where b2c also takes one parameter. In Lisp, you end up with something more like
(defun a->c (x)
(b->c (a->b x)))
This is fine for being concise but it doesn't preserve the (apparent) logical ordering of \(a\to b\to c\). We could do that with a change to something more imperative, such as
(defun a->c (x)
(let ((b (a->b x)))
(b->c b)))
and this works okay. However, I would like to write
(defun a->c (x)
(>>
(a->b x)
(b->c)))
Or,
(defun a->e (x)
(>> :pipe-in
(a->b x)
(b->c)
(c->d :pipe-in 0.0625d0)
(d->e)))
where we want this last one to expand into
(D->E (C->D (B->C (A->B X)) 0.0625))
I've introduced a keyword so that we can dictate which parameter of the next function the previous result goes into. You can specify any keyword to use as a pipe-in parameter, but make sure you don't use a keyword of one of the functions being called in the sequence—it'll really mess with your mind. Here's the macro definition along with an error condition:
(defun a->c (x)
(>>
(a->b x)
(b->c)))
Or,
(defun a->e (x)
(>> :pipe-in
(a->b x)
(b->c)
(c->d :pipe-in 0.0625d0)
(d->e)))
where we want this last one to expand into
(D->E (C->D (B->C (A->B X)) 0.0625))
I've introduced a keyword so that we can dictate which parameter of the next function the previous result goes into. You can specify any keyword to use as a pipe-in parameter, but make sure you don't use a keyword of one of the functions being called in the sequence—it'll really mess with your mind. Here's the macro definition along with an error condition:
To test that the code works, we can try this at the REPL,
CL-USER> (macroexpand-1 '(>> :pipe-in
(a->b x)
(b->c)
(c->d :pipe-in 0.0625)
(d->e)))
(D->E (C->D (B->C (A->B X)) 0.0625))
This macro does not attempt to handle multiple return values—though such a development might lead to an interesting result. It might be as easy as introducing more keywords.