I think this is a complete non-issue? I.e. both protocols are mostly equivalent under code transformations that the julia compiler is good at. Consider:
#julia iteration
next = vanilla_iterate(xs)
while next != nothing
x, state = next
# do something with x
next = vanilla_iterate(xs, state)
end
#mikeInnes' original proposal
itr = mi_iterator(xs)
while true
next = mi_iterate(itr) #problematic line
next == nothing && break
x, itr = next
# do something with x
end
#mikeInnes proposal (improved)
itr = mi_iterator(xs)
next = mi_iterate(itr)
while next != nothing
x, itr = next
# do something with x
next = mi_iterate(itr)
end
Now suppose we have a code construction that works well with the mikeInnes proposal. Then we can simply define
@inline vanilla_iterator(xs) = mi_iterate(mi_iterator(xs))
@inline vanilla_iterate(xs, state) = mi_iterate(state)
If you plug that into the snippet for vanilla iteration, you get:
itr = mi_iterator(xs)
next = mi_iterate(itr)
while next != nothing
x, state = next
# do something with x
next = mi_iterate(state)
end
So that’s awesome! Julia iteration is at least as good / flexible / performant as Mike’s proposal in cases where type inference works.
Now next: Suppose julia moved to my modification of Mike’s proposal. Then we modify old-style iteration in the following way:
struct IterHelper{T} item::T end
@inline mi_iterator(xs) = IterHelper(xs)
@inline mi_iterate(wrappedXS::IterHelper) = begin
xs = wrappedXS.item
next = vanilla_iterate(xs)
if next == nothing
nothing
else
nextItem, nextState = next
(nextItem, (xs, nextState))
end
end
@inline mi_iterate(itr) = begin
xs, state = itr
next = vanilla_iterate(xs, state)
if next == nothing
nothing
else
nextItem, nextState = next
(nextItem, (xs, nextState))
end
end
then let’s plug that into the modified iteration protocol proposal:
itr = IterHelper(xs)
next = vanilla_iterate(xs)
if next != nothing
x, state = next
next = (x, (xs, state))
end
while next != nothing
x, itr = next
# do something with x
xs, state = itr
next = vanilla_iterate(xs, state)
if next != nothing
nextItem, nextState = next
next = (nextItem, (xs, nextState))
end
end
It is quite clear how the compiler will continue to simplify that and end up with the original vanilla julia iteration protocol.
If you try the same with Mike’s original proposal, then you will encounter a problem in the marked problematic line
next = mi_iterate(itr) #problematic line
The problem is that itr
is either of type IterHelper
or of type typeof((xs, state))
. This is a problematic type instability, because the necessary compiler transforms (e.g. loop rotate) to double this line and make it type stable afaiu only happen after type inference. Maybe julia emits good enough code with suboptimal type inference that llvm can fix that; maybe not. I wouldn’t bet on it.
But maybe the compiler improved in that regard since I last looked?
cc @MikeInnes since you disabled comments on your blog post.