-
-
Notifications
You must be signed in to change notification settings - Fork 33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Experiment: Playing with currying, chaining and underscores #148
base: main
Are you sure you want to change the base?
Conversation
Is this intended to implement the original proposal in that Discourse thread, or a variation on that theme? To me this looks a bit different from the original proposal. |
It was meant to implement the flavor of the original proposal, more or less (but I've also added some underscore processing which is clearly unrelated). Think of this as a playground - we can change things and implement multiple syntaxes at the same time to get some idea of how different things play out. I found the suggested parsing/precedence of I probably need to read the original proposal more carefully too 😅 |
2fe6b31
to
3c6e6f9
Compare
Noting that this syntax can be used without data on the left hand side to create a first class data pipeline, I've upgraded this to allow syntax such as
Lowering to
Also I've cleaned up the lowering a bit such that we're creating |
Here's my comparison of this PR to the original Discourse proposal: https://discourse.julialang.org/t/fixing-the-piping-chaining-issue/89654/178 TLDR: *By "front pipe" and "back pipe", I mean having the following syntax transformations in the parser: # front pipe:
a \> f(b, c)
# parses as
f(a, b, c)
# back pipe:
c \>> f(a, b)
# parses as
f(a, b, c) |
Thanks!
A quibble: we wouldn't parse it this way because an important feature of the parser is to reflect the surface syntax such that macros can inspect and manipulate it. So the parser should emit a representation of However, we could lower it to |
Good point, thanks. That makes sense! |
So, it turns out that the lowering adopted here can provide fundamentally more efficient data pipelines than normal piping by allowing rewrite rules to be added as methods to The latest commit shows how |
Though rewrite rules are a symbolic simplification step which is not ideal in some ways. Transducers are still fundamentally more composable than a pile of rewrite rules. I wonder whether we could get them in here somehow. |
One could imagine the underscore currying lowering to a x |> map(sqrt, _) |> reduce(+, _) would be equivalent to something like this, FixArgs(reduce, (1, ), +)(
FixArgs(map, (1, ), sqrt)(x)
) so there doesn't seem to be a way to intercept the
Yeah, perhaps transducers are the real way to make piping in Base more powerful. (Well, Base piping is already powerful, it's just the syntax with a bunch of anonymous functions that is annoying.) Although it seems that the usage of Anyhow, maybe the moral of the story is that if you use transducers, then you don't even need piping, you just need function composition. It would be cool if in Julia 2.0 |
@c42f I have another proposal I'd like to try, which is essentially to move I might try to give it a shot in a fork myself because that seems like a cool learning experience, but I hope it's ok if I can ask you questions on the way :) |
@adienes go for it if you like, I think it's a good learning experience :-) For a new syntax to be really useful it's got to be somehow significantly better than the associated macro version (eg, somehow more composable or succinct). Unsure this would be the case for |
A super hacky, quick implementation of some ideas from https://discourse.julialang.org/t/fixing-the-piping-chaining-issue Parse chains of `/>` and `\>` at the rough same precedence as `|>`, but treat them as a "frontfix/backfix operator" for function calls such that the succeeding function call becomes curried in first or last argument. Thus, the following x /> f(y) \> g(z) is parsed as (chain x (/> f y) (\> g z)) and lowered to the equivalent of chain(x, a->f(a, y), b->g(z, b)) Also add lowering of underscore as strictly tight-binding placeholder syntax. (Super hacky - more forms should be allowed! This is just for experimentation).
This provides an example of how such pipelines can be fundamentally more efficient than normal piping syntax.
25e4aba
to
a72c62a
Compare
I've updated this to use I've also merged the changes from #199 in here so we have something a bit more coherent in terms of underscore lowering. And made function calls to the right of julia> 1:10 />> filter(isodd) />> map(_^2)
5-element Vector{Int64}:
1
9
25
49
81 And julia> data = [(a=i,b=2i) for i=1:10];
julia> data />> filter(isodd(_.a^2))
5-element Vector{NamedTuple{(:a, :b), Tuple{Int64, Int64}}}:
(a = 1, b = 2)
(a = 3, b = 6)
(a = 5, b = 10)
(a = 7, b = 14)
(a = 9, b = 18)
julia> data />> filter(isodd(_.a^2)) />> sum(_.b)
50 |
This makes it visually clear which argument is being piped into
a72c62a
to
89054a8
Compare
is #199 complementary, incompatible, or orthogonal to this proposal? I must admit I am not at all a fan of the aesthetics of piping into a headless |
It's largely complimentary and I've merged the changes from that PR in here as well to see how they'd play out together. |
A super hacky, quick implementation of "pipefirst / pipelast" ideas inspired by
https://discourse.julialang.org/t/fixing-the-piping-chaining-issue
Tiny demo:
The meaning of
/>>
is roughly "pipelast", so the above expression is likex |> (y->filter(isodd, y)) |> (y->map(z->z^2, y))
though with different lowering. Similarly/>
is "pipefirst".[Note: in the original version of this PR,
/>>
was spelled\>
but this is visually confusing.]Implementation
Parse chains of
/>
and/>>
at the rough same precedence as|>
, treating them as a currying operator for function calls such that the succeeding function call becomes curried with the only free argument as either the first or last, respectively. Thus, the followingis parsed as
and lowered to
Also, this can be used without data on the left hand side to create a first class data pipeline:
Lowers to
Additionally, add lowering of underscore as strictly tight-binding placeholder syntax. (Super hacky - more forms should be allowed! This is just for experimentation).
Note, I don't really expect to ever merge this, given how difficult JuliaLang/julia#24990 has proven 😬
It's just an experiment for people to play with.
You can try this by checking out this branch and using
(With this setup, you can also edit JuliaSyntax.jl to play with different lowering scenarios and see them almost immediately in the REPL using Revise. (May need to run a command twice for Revise to pick things up.))