Why for loops don't return the last evaluated expression as their value?

I was wondering why for loops don’t return the last evaluated expression – similar to if statements, for example.

1 Like

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.

2 Likes

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).

1 Like

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.

1 Like

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.

1 Like

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.