I appreciate the help, I tried to separate to independent questions. One question was the “type annotation with no name”, the other was “semi colon in the function declaration”. I am seeing how the first works. Can you clue me in on the semi colon?
julia> f(a, b) = a + b
f (generic function with 1 method)
julia> g(a; b) = a + b
g (generic function with 1 method)
julia> f(1, 2)
3
julia> g(1, 2)
ERROR: MethodError: no method matching g(::Int64, ::Int64)
Closest candidates are:
g(::Any; b)
@ Main REPL[2]:1
Stacktrace:
[1] top-level scope
@ REPL[4]:1
julia> g(1; b=2)
3
Can you explain the difference between comma and semi-colon here? In @Xing_Shi_Cai’s question, I would expect g(1; b=2) to work the same either way, and the semi-colon seems mostly useful for the human reader.
But in the sum, I don’t understand how it’s parsed. If I use an empty collection the init seems to be treated the same both ways:
julia> sum([]; init=1)
1
julia> sum([]; init=0)
0
What is going on with sum(i for i = 1:2, init = 2)?
The whole of i for i = 1:2, init = 2 is parsed as a comprehension with two loops. You can use a comma there if you separately ensure the comprehension doesn’t gobble it up — it’s effectively a precedence issue:
julia> sum((i for i = 1:2), init = 2)
5
The explicit semicolons are useful to ensure that everything that follows is a kwarg, and they also solve this particular precedence problem. They’re required to separate optional args from kwargs in function definitions. And at call sites they enable the special variable-name-is-kwarg-name syntax:
julia> init = 2
2
julia> sum(i for i = 1:2; init)
5
Thanks, I never would have guessed two comprehensions. I can now kind of understand it, since it is kind of like this:
julia> sum(i for i in 1:2, j in 1:2)
6
Part of my discomfort is that init = 2 looks like a left assignment, just like a statement x = 2 except it’s not. I don’t expect init = 2 to somehow affect i, because who said so?
Also, this interpretation seems to be particular to sum and not to comprehensions in general. For example, this errors: [i for i in 1:2; init=1] but it’s fine in sum.
I find BenchmarkTools.@btime more sensible, with setup = (i = 2). I suppose this is for macros, so I might prefer something like @sum(i for i in 1:2; setup = (i = 2)), which I find much clearer. Maybe the @ is ugly but I think it helps signal that we’re only initializing (EDIT: the dummy variable) i within the comprehension but not outside, and that setup is merely a keyword/container for that info.
Or maybe there is a better way to think of init = 2 so it feels more natural.
Ehhh, that’s still pretty confused. There are three very different concepts happening here:
You can specify multiple loops in a single generator.
The generator (i,j) for i in 1:2, j in 3:4 is the same as:
for j in 3:4
for i in 1:2
(i,j)
end
end
And that’s the same as:
for j in 3:4, i in 1:2
(i,j)
end
julia> foreach(println, ((i,j) for i in 1:2, j in 3:4))
(1, 3)
(2, 3)
(1, 4)
(2, 4)
julia> for j in 3:4
for i in 1:2
println((i,j))
end
end
(1, 3)
(2, 3)
(1, 4)
(2, 4)
julia> for j in 3:4, i in 1:2
println((i,j))
end
(1, 3)
(2, 3)
(1, 4)
(2, 4)
And then, of course, you can exchange ins and =s in all three.
The sum function can take an init keyword that is the “initializer” of your sum. t’s not affecting the i in your generator at all. It’s just the thing that the first element from the generator gets added to. (As an aside, it’s actually required to be an additive identity as it may get incorporated more than once with a parallel algorithm.)
In case it’s not clear, Julia’s syntax allows in, ∈ or = to be used interchangeably in for loops and generator expressions. As documented here: Repeated Evaluation: Loops. I agree with you though, seeing = used for this purpose makes me uncomfortable, too
I think this is probably disambiguated the wrong way. The parser should prefer the keyword argument interpretation here. Passing a generator with nested loops to a function is weird and if you really meant that you can either parenthesize it or use multiple for keywords.