New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Align Proc.new(&block)'s behaviour with other captured blocks #10263
Conversation
Can we merge only the new |
@straight-shoota I think both can go at the same time. A compiler compiling this new compiler has the hardcoded logic for that. |
Yeah okay, master compiler already can't compile 0.35.1 anymore. In the future I would suggest to move slower with changes affecting the interface between stdlib and the compiler and always have one release in between which works in both directions. |
A new compiler isn't expected to compile an older compiler. |
No, that's not necessary. But it would be nice if a new compiler would be somewhat compatible with stdlib of the previous release. |
That is necessary because the previous compiler is used with the current std-lib to build the new version. |
Yeah well, as I said, current master does not compile 0.35.1 stdlib. |
Ok, I see want you mean, but in what scenario you would need for that to work? |
Bug hunting 😄 |
It would be great to at least clearly document the migration process in the PR. |
I don't think there's any migration process? A hardcode method in Proc is removed, but then an equivalent method in std is added. |
Oh, sorry I confused this PR with a breaking change. |
A side effect of this change is that, I guess reasonable, we lost the possibility to use This used to work: alias P = (Int32) -> Int32
p = P.new do |n|
return 0 if n < 0
n
end But after this change the user will see
So is either reworking the code to avoid using p = ->(n : Int32) {
return 0 if n < 0
n
} If someone thinks that this going in the wrong direction we can revert. But I think it make sense and we are still improving the consistency. |
Interesting! This is actually going in the right direction. p = ->(n : Int32) {
return 0 if n < 0
n
} To return from a block one should always use The reason is that |
What is the problem with allowing return in proc literals? |
I think it's just confusing. Consider this in Ruby: def foo
->(n) {
return 10 if n < 0
n
}.call(-1)
proc { |n|
return 20 if n < 0
n
}.call(-1)
bar do |n|
return 30 if n < 0
n
end
30
end
def bar
yield -1
end
p foo What do you expect the return value to be? Now try it and tell me whether it's intuitive. The problem is that when you see |
Crystal doesn't seem to support # Crystal
foo = ->(n : Int32) do
next 0 if n < 0 # Error: invalid next
n
end
foo = Proc(Int32, Int32).new do |n|
next 0 if n < 0 # okay after this PR (good!)
n
end
foo.call(-1) # => 0 So @bcardiff if you replace The problem is both Crystal and Ruby seem to behave as if |
I read proc literal as lambdas. So I would expect to be able to use return as with any def. But I see the benefits on keeping the limitation. Specially in contexts where the proc literal body might be generated by a macro expansion. |
Defines
Proc.new(&block)
in the standard library instead of hard-coding its behaviour in the compiler, c.f. #10001 (comment).Resolves #9813. Although unconfirmed, this constructor should also benefit from #10251.
There is still compiler magic involvingProc.new(&block)
in the cleanup transformer:->
literals; the reasonProc.new
used to work is because the compiler internally rewrote these calls to->
literals.As per request, all definitions ofProc.new(&block)
must match the standard library's definition exactly; that is, it must take only a block argument matching theProc
's instantiated type, return that block, and do nothing else. This shouldn't affect any existing code since the constructor is definitely not meant to be redefined.Additionally, the compiler will now check for closured vars in any captured block inside a lib fun call as long as the enclosing method returns the captured block and does nothing else. This applies for
Proc.new(&)
, but also cases like the following are detected:This means there is now far less compiler magic involving
Proc.new(&block)
(probably none after this PR).