-
-
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
Document function argument evaluation order #24603
Conversation
I asked @JeffBezanson about this at the latest Julia meetup in Cambridge. If I understood correctly, starting with 0.7 the order of function argument evaluation is always lexically left to right.
it turns out this is subtle – it looks like the explicitly passed arguments are evaluated first, left to right, followed by the default values for missing arguments.
@@ -328,6 +328,22 @@ Notice the extra set of parentheses in the definition of `range`. | |||
Without those, `range` would be a two-argument function, and this example would | |||
not work. | |||
|
|||
## Argument evaluation order | |||
|
|||
All arguments passed to a function are evaluated in lexical left-to-right order. |
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.
Would be nice to show an example with keyword arguments as well. (#23104 was where this changed IIRC.)
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.
This example is also very slightly misleading --- the reason d default
gets printed after is that it actually happens in a subsequent method call. Due to the way optional arguments work, that will also always be lexically later, but it's technically a different reason.
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.
That's a good point. I tried to pare this example down from a more complicated one with even more subtle behavior involving specifying keyword arguments out of order...
Is it fair to say that the rules can be summarized as: first evaluate all passed arguments from left to right wrt. the order at the call site, and then evaluate all default values from left to right wrt. the order in the method definition, with all defaults being evaluated in a separate method call due to the mechanics of default 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.
Yes, that's right. Really, every case is intuitive except maybe f(a, b=1, c, d=2, ...)
where keyword and positional arguments are mixed.
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.
Unrelatedly, I wonder if we should call them "named arguments" for consistency with named tuples. There's precedent for that nomenclature, though both Python and Ruby opt for "keyword argument" instead.
I found an interesting counter-example to left-to-right evaluation, because of julia> a = zeros(10); i=1; a[i+=1] = (i+=1); maximum(a)
2.0
julia> a = zeros(10); i=1; a[i+=1] += (i+=1); maximum(a)
3.0 |
The lowering could be changed so that the evaluation is syntactic left-to-right, which might be better. |
Then you could not write methods like |
Why wouldn't you be able to write methods like that? |
Nevermind, I thought you suggested changing the order of the |
Being pedantic, assignment syntax is not function call syntax, so the same rule does not necessarily apply. I agree it would be better to change it, though that would be breaking. |
Of course, it doesn't have to follow the same rule but consistently doing left-to-right evaluation in syntactic order is probably the least surprising thing we can do where it's possible to do so. |
Bonus material: When is the iteration of argument splatting evaluated? That is, in |
Keyword arguments are evaluated in the order in which they appear in the function definition? Or the order in which they appear at the call site? Also, it would be nice to merge this. |
Would you be up for finishing it? Here's an example I put together to show the combinations that you can use: julia> f(a, b, c...=println("Optional c's after b=$b"); D=println("kw D"), kwargs...) = println("$a & $b & $c & $D & $kwargs")
f (generic function with 5 methods)
julia> (println("f"); f)(println("A"), B=println("B"), println("C"));
f
A
B
C
Optional c's after b=nothing
kw D
nothing & nothing & (nothing,) & nothing & Base.Pairs(:B => nothing)
julia> (println("f"); f)(println("A"), B=println("B"), println("C"), D=println("D"), println("E"));
f
A
B
C
D
E
nothing & nothing & (nothing,) & nothing & Base.Pairs(:B => nothing) |
I asked @JeffBezanson about this at the latest Julia meetup in Cambridge. If I understood correctly, starting with 0.7 the order of function argument evaluation is always lexically left to right.