Possible bug: return inside an array comprehension

bug

#1

I wonder if the behaviour below is the intended behaviour for return inside an array comprehension. It seems to me that f1 and f2 should behave in the same way, terminating the function as soon as return is reached once. This syntax might be useful when using short-circuit if statements inside an array comprehension, returning something different for each condition, where the conditions follow an indexable pattern, that can be looped over.

julia> f1() = [return 1 for i in 1:5]
f1 (generic function with 1 method)

julia> f1()
5-element Array{Int64,1}:
 1
 1
 1
 1
 1
 
julia> function f2()
           for i in 1:5
               return 1
           end
       end
f2 (generic function with 1 method)

julia> f2()
1

#2

The current behavior is expected. If it is confusing, we can make it an error.


#3

I mean it is kind of confusing especially that [return 1 for i in 1:5] and return [1 for i in 1:5] behave the same way, so I don’t see a useful way in which this behaviour could be used. But the more intuitive behaviour would be the one resembled by f2. Changing its behaviour is not an option?

Edited to add: I guess making it an error at least guards against misinterpretation, so if nothing more can happen before v0.6, then it is still better the current situation.


#4

No. Comprehension is a wrapper around generator and the body is used to generate a closure. return in there never does what you want it to and cannot be implemented that way. Making it an error is certainly possible and will be consistent with 0.4.


#5

I just thought it was worth mentioning. The same syntax works as expected with throw() instead of return which I believe is not really a surprise or a problem.


#6

Please don’t make it an error unless there is a compelling reason, conceptual equivalence to a closure is useful and should be retained, as it makes reasoning about comprehensions more transparent. Eg a macro could use return in the body, etc.


#7

It doesn’t make sense to me either that the return should be completely ignored, and I don’t think disallowing return in this case really changes any reasoning about the comprehension.
It seems like with a return 1, it should return a 1, not [1,1,1,1,1], if you have a throw it doesn’t try to ignore the throw and continue execution either. Since returning a single value would be type unstable and not useful, it seems best to have the compiler give an error in this case.


#8

It is not ignored. It is called from the (implicit) closure, which returns.


#9

OK, I understand your point now, but I haven’t found anything in the documentation about comprehensions or generators that discusses that conceptual equivalence to a closure. Maybe you could make a PR and add that to the docs, so that the rationale behind this behavior is clear? (even if it is still surprising to see)
Maybe this is also something that Lint.jl could warn about, since the return here is not useful at all, just confusing.


#10

I don’t know if it is meant to stay this way or just the current implementation. AFAICT the closures should not affect anything else (eg scoping), just the effect of a return.


#11

return can show up in other potentially unclear (and unusual) situations, too.

module:

0.6.0-rc3.0> module X
               a = 1
               b = 2
               return
               c = 3
             end

0.6.0-rc3.0> X.c
3

which looks like it doesn’t do anything.

let:

0.6.0-rc3.0> let x = 1
               return
               println("Yo!")
             end

doesn’t print anything, but probably makes sense though.

type:

0.6.0-rc3.0> type Foo
               x::Int
               return
               y::Int
             end

0.6.0-rc3.0> Foo(1, 2)
ERROR: MethodError: no method matching Foo(::Int64, ::Int64)