I was wondering why for
loops don’t return the last evaluated expression – similar to if
statements, for example.
This is just speculation, but perhaps it’s because it’s not clear what to return when the iterator is empty? That is, if you wrote:
y = for i in 1:N
i + 1
end
then what should y
be if N < 1
(in which case the body of the loop is never executed)? We could decide that nothing
is a sensible default, but then that would make assigning the result of the loop inherently type-unstable, since the returned type (Int or Void) would depend on the value of N.
Correct, it’s an inherent type instability. See also:
https://github.com/JuliaLang/julia/pull/23260
This PR implements break-with-value and for with else, which together mean that it actually does make sense for a loop to return a value. Still a speculative feature, but we’ve got an implementation and may add this feature in the future (1.x).
Thanks
@rdeits How would that be any different from
z = if false
"moo"
end
Only through the fact that one can optionally add an else
as a safeguard against type instability?
@StefanKarpinski Can’t remember seeing these constructs in any other languages that I’ve used, but I find them super-cool. Very useful syntax innovations, I’d love to see them available.
That’s a good point – it’s not really any different. I suppose we could make loops return the last value evaluated in the loop. It might be annoying in the REPL when you write loops.
That would certainly make the behavior consistent. But to be truly useful, in the absence of more exotic for-else
or break-with-value
constructs, break
would also have to return the last evaluated expression. That’s how I stumbled into this actually, trying to do something in the line of:
_path, _extension = for file_extension in [".html", ".md", ".mt"]
if isfile(file_path * file_extension)
file_path * file_extension, file_extension
break
end
end
To me it looks like a legit usage of the language, something I definitely expected to work and it took me quite a bit to understand the error I was getting.
Looks like this might be a good fit for the API consistency review issue.
I can certainly see the appeal — I occasionally think about syntactic extensions that would make sense in a given context, but at the same time I admire the restraint in the design of Julia that kept complexity and clever variants of syntax with subtly different semantics to a minimum.
Is
result = some_default
for elt in itr
...
end
result
that inconvenient? I find it to be a good trade-off between readability and compactness.
I could also imagine a Lisp-like
@prog1 result = some_default for elt in itr
...
end
but again, I don’t think it is worth it.
You could do it like in Scala where if you want the for to return a value you need a yield statement inside the loop.
Yes, that’s what the PR I linked to above does.