-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Move v0
to an init
keyword argument for reduce
, etc
#27711
Conversation
1a47867
to
42389c2
Compare
base/abstractset.jl
Outdated
@@ -59,7 +59,7 @@ julia> a | |||
Set([7, 4, 3, 5, 1]) | |||
``` | |||
""" | |||
union!(s::AbstractSet, sets...) = foldl(union!, s, sets) | |||
union!(s::AbstractSet, sets...) = foldl(union!, sets; init = s) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally we don't use spaces around =
for keyword arguments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks
base/reduce.jl
Outdated
Singleton used for the purpose of indicating to the [`reduce`](@ref) family of functions | ||
that no initial value has been specified for the reduction. | ||
""" | ||
struct NoInit; end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I must say I'm not a fan of this. This feels like a slippery slope to singletons for every possible situation. Instead I think it makes sense to use nothing
here, which has become pretty conventional. If someone wants to actually have nothing
as their initial value, they should use Some(nothing)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah - I'm definitely in two minds about it!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Though, were you suggesting we unwrap Something
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. So init=nothing
would mean "no initial value provided," whereas init=Some(nothing)
would mean "the initial value is provided and is nothing
." That's pretty much the use case for Some
. 🙂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK - bear with me, I'm trying to get my head around this pattern. If init = Some(1)
do we use 1
or Some(1)
for the inital value? How do I provide Some(nothing)
as the initial element if I need to do that? I'll also need to find the best place to handle the unwrapping elegantly. Do we have a convenience function for unwrapping zero or one Some
s?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You really only ever need to worry about wrapping with Some
if you're dealing explicitly with nothing
s, which should be an incredibly uncommon case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I'm not going to express any preference towards using kwargs or not for this, if we do this, let's do without any special magic constants. Here's pseudo code for it (assuming I approximately get the definition of mapfoldl
right):
function mapfoldl(f, op, itr; init...)
if length(init) == 1
v = init.init
init = NamedTuple()
end
isempty(init) || throw(ArgumentError("incorrect kwarg")) # MethodError?
for x in itr
x = f(x)
if @isdefined v
v = op(v, x)
else
v = x
end
end
return v
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Jameson, I like it!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's kind of weird that we can do this variadic keyword args but not standard ones. Maybe that's fine but it feels kind of messy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it will be a slightly messier implementation but it’s simpler for end users, which is what gains my vote here.
It does make me wonder if the “pattern” could be supported in a more straightforward way by the language.
42389c2
to
f1bafe7
Compare
test/reduce.jl
Outdated
@@ -118,12 +118,12 @@ sum2(itr) = invoke(sum, Tuple{Any}, itr) | |||
plus(x,y) = x + y | |||
sum3(A) = reduce(plus, A) | |||
sum4(itr) = invoke(reduce, Tuple{Function, Any}, plus, itr) | |||
sum5(A) = reduce(plus, 0, A) | |||
sum5(A) = reduce(plus, A; int=0) | |||
sum6(itr) = invoke(reduce, Tuple{Function, Int, Any}, plus, 0, itr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can someone help me with this? What's the form for invoke
with keyword arguments?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe you have to use the gensym'd name, i.e. Symbol("#kw##reduce")
, then specify the keyword arguments' types with a NamedTuple
type. See for example how functions with keyword arguments are precompiled in base/precompile.jl
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, it took me a while to grok but I think I've got this sorted now. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this not work? invoke(reduce, Tuple{Function, Any}, plus, itr, init=0)
test/reduce.jl
Outdated
sum9(A) = mapreduce(x->x, plus, 0, A) | ||
sum10(itr) = invoke(mapreduce, Tuple{Function, Function, Int, Any}, x->x,plus,0,itr) | ||
sum9(A) = mapreduce(x->x, plus, A; init=0) | ||
sum10(itr) = invoke(mapreduce, Tuple{Function, Function, Int, Any}, x->x,plus, 0, itr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly here, invoke
with keywords?
96dda6f
to
8598a5e
Compare
8598a5e
to
d37d5e9
Compare
OK, I've fixed some tests in the first commit. In the second commit I broke everything again. Here I'm trying to get rid of |
Like [`reduce`](@ref), but with guaranteed left associativity. `v0` will be used | ||
exactly once. | ||
""" | ||
mapfoldl(f, op, itr; [init]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we use brackets to signal that keyword arguments are optional, see e.g. sum
. I'm not sure whether we should or not, but better be consistent about it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was following the examples in titlecase
, replace
, replace!
(and I think some others). Let's discuss and deal with whatever is the best documentation pattern here separately.
Would it make sense to also add an |
18c449f
to
afacec9
Compare
@StefanKarpinski is it likely we will be able to get this into v0.7-final? (Otherwise it's v2.0) |
Seems like a decent idea. Though, lets see if we can merge this quickly and go from there (also, that's potential 1.x material). |
Seems like a good idea to me. |
@nanosoldier |
Had to restart the server. @nanosoldier |
afacec9
to
b943e6f
Compare
Your benchmark job has completed - possible performance regressions were detected. A full report can be found here. cc @ararslan |
The large regressions shown by nanosoldier are all caused by deprecations affecting BaseBenchmarks.jl. We can fix that separately, but I'm not sure how to use two different versions of BaseBenchmarks on the one nanosoldier comparison job? I have checked performance of (Actually, to me that highlights a design issue with our CI... if the tests we use to check PRs aren't in the same repo (or a git submodule) then how can a single github PR possibly be self-contained?) |
b943e6f
to
9f6bef6
Compare
The initial value `v0` has been moved to a keyword argument `init` in `reduce`, `mapreduce`, `foldl`, `mapfoldl`, `foldr` and `mapfoldr`. (This will allow the future addition of multiple input iterators like we allow for `map`).
9f6bef6
to
1a582a3
Compare
Fingers crossed for CI this time. |
Hack utilizes varargs form of keyword arguments.
1a582a3
to
037ca79
Compare
All checks passed. I will merge. We'll need to update BaseBenchmarks.jl to reflect these deprecations. |
I forgot about |
The initial value
v0
has been moved to a keyword argumentinit
inreduce
,mapreduce
,foldl
,mapfoldl
,foldr
andmapfoldr
.See related discussions at #27704 and refs within. In particular, this will allow the future addition of multiple input iterators to
mapreduce
just like we allow formap
. We will need to let the deprecations expire before implementing all of #27704. We wouldn't have considered this as practical before because keyword arguments weren't type stable.To facilitate the optional keyword argument, I introduced a new singleton calledNoInit
. It seemed unsafe to usenothing
as the default value to indicate no specified value, becausenothing
is beginning to mean something in certain situations and I wanted this to be as generic as possible.