I'm working on a syntax for racket using pipes similar to unix, something like this:
> ("FOO" > string-replace "O" "E" > string-append "x" > string-downcase)
"feex"
Here is a brute force solution, which supports procedures with 2, 1, and 0 (extra) arguments:
(require (prefix-in racket/base/ racket/base) syntax/parse/define)
(define-syntax-parser #%app
[(_ data (~literal >) proc a b (~literal >) rest ...) #'(#%app (proc data a b) > rest ...)]
[(_ data (~literal >) proc a (~literal >) rest ...) #'(#%app (proc data a) > rest ...)]
[(_ data (~literal >) proc (~literal >) rest ...) #'(#%app (proc data) > rest ...)]
[(_ data (~literal >) proc rest ...) #'(#%app proc data rest ...)]
[(_ rest ...) #'(racket/base/#%app rest ...)])
The problem is finding the next pipe, because the syntax pattern does not allow multiple ... patterns. The macro needs to know where the next pipe is to close the form for the first one. Unless there is a way to build partial syntax objects with unmatched parens?
I can nest the ellipses, but then I have to use extra parens:
(define-syntax-parser #%app
[(_ data (~literal >) (proc params ...) > rest ...) #'(#%app (proc data params ...) > rest ...)]
[(_ data (~literal >) proc rest ...) #'(#%app proc data rest ...)]
[(_ rest ...) #'(racket/base/#%app rest ...)])
> ("FOO" > (string-replace "O" "E") > (string-append "x") > string-downcase)
"feex"
Is there any way to do this without the extra parens?
I'm aware of clojure's threading macros, but they are difficult to follow if you have to nest them.
EDIT: the solution to this problem is now available as a racket package and on github
You can use the
~seqpattern combined with elipses to match without parens. For example:Will require that
splitis given an even number of arguments, which will then be re-arranged and built into a list:Of course, be mindful
~seqisn't magic and has limited support for backtracking when parsing. But in principle you should be able to do something like: