-
Notifications
You must be signed in to change notification settings - Fork 3
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
Interpolate do
syntax closures into single _
#15
base: master
Are you sure you want to change the base?
Conversation
This allows a single _ to be treated as the slot into which the closure created by do syntax is inserted. It's somewhat overloaded in that `@_ map(_, xs)` normally means the _ is the identity, while `@_ map(_, xs) do ...` would mean the same as normal do syntax without the `_`, but conceptually we're marking a "slot of map" to receive a closure so it could be acceptably consistent. Also, it seems likely to be extremely convenient.
Personally, I’m tempted to say this should just be a separate macro from |
I think it'd be nice to have this if this is compatible with everything else. Otherwise, I think I agree with @MasonProtter that separating it out from In particular, I wonder how it interacts with @_ function ((i, d),)
x = get(d, :a, nothing)
x === nothing && return nothing
y = get(x, :b, nothing)
y === nothing && return nothing
z = tryparse(Float64, y)
z === nothing && return nothing
return (i, z)
end |> mapreduce(__, _, pairs(data); init = nothing) do a, b
a === nothing && return b
b === nothing && return a
a[2] < b[2] ? b : a
end (I'm expecting |
Yes that should work (but I had a bug, oops. Fixed now). It's interesting that you can place the closures on both sides 😆 Certainly confusing to look at IMO, but no reason to disallow it. julia> @_ function (x)
@show x
x^2
end |> mapreduce(__, _, [1,2,3]) do x,y
x+y
end
x = 1
x = 2
x = 3
14 |
Overall I think supporting
My only objection to that is that macros don't really compose naturally when they act on the same type of syntax for the same reasons. So in some ways it's tempting to build all the closely related behaviors together into one macro. |
Don't worry I won't do this in important code 😆. It's just that it was the first example I could think of to use I guess more practical example is @_ ... |> groupreduce(x -> x.key, _, __) do a, b
a.value < b.value ? b : a
end BTW, it looks like this does not work? @_ collect(Filter(_), -3:3) do x
x > 0
end
# => collect(Filter(x -> x > 0), -3:3) I guess allowing this introduces more inconsistencies, though. I wondered a bit if it makes sense to add @_ collect(Filter(__), -3:3) do x
x > 0
end would work. But then the @_ ... |> itr -> groupreduce(x -> x.key, __, itr) do a, b
a.value < b.value ? b : a
end which is not nice... That said, extending the crazy thought, I wonder if it makes sense to use the number of
We then have @_ ... |> groupreduce(_.key, __, ___) do a, b
a.value < b.value ? b : a
end Well, too bad that it's hard to count the number of [*1] That's how I interpret them. |
Haha! Yes... I did consider that the closure created by
Yes. Though the fact that you might want this to work kind of suggests to me that this PR isn't really mergeable. It's just too confusing and overloaded, in a similar way that #4 was too confusing. I feel like the best option may be just to go with an ugly but very clear identifier like I think the |
Yeah agreed. |
TBH, I think it's OK to not do everything at AST. Though this is probably mainly because I can tweak Transducers.jl API to add @_ -3:3 |> Filter(_ > 0) |> collect
# or even
-3:3 |>
Filter() do x
x > 0
end |>
collect
I'm actually experimenting API for challenging this :). With julia> averaging = # transducer version of `OnlineStats.Mean`
function add_average((sum, count), x)
(sum + x, count + 1)
end |>
wheninit() do
(Init(+), 0) # attach "loop header"
end |>
whencomplete() do (sum, count)
sum / count # attach "loop footer"
end |>
whencombine() do (sum1, count1), (sum2, count2)
(sum1 + sum2), (count1 + count2) # for threaded reduce
end; I think this would be more readable than kwargs-based API especially when you have more complex function bodies julia> averaging = AdHocRF(
oninit = () -> (Init(+), 0),
complete = (sum, count) -> sum / count,
combine = ((sum1, count1), (sum2, count2)) -> ((sum1 + sum2), (count1 + count2)),
) do (sum, count), x
(sum + x, count + 1)
end; For groupreduce, maybe we can make something julia> @_ 1:100 |>
Map((k = gcd(_, 42), v = _)) |>
grouped(_.k, __) .|>
Map(_.v) .|>
maximum work. Then, since each expression between |
This allows a single
_
to be treated as the slot into which the closure created by do syntax is inserted.It's somewhat overloaded in that
@_ map(_, xs)
normally means the_
is the identity, while@_ map(_, xs) do ...
would mean the same as normal do syntax without the_
, but conceptually we're marking a "slot of map" to receive a closure so it could be acceptably consistent.Also, it seems likely to be extremely convenient.
A more realistic example would be a function which takes multiple functions as arguments (eg
mapreduce(f, _, xs) do ...
) allowing the second argument to be given with thedo
block.CC @tkf @MasonProtter and anyone else interested — thoughts? Does this seem acceptably consistent?
Closes #4 by replacing it