Em 15-12-2011 19:23, Gary Wright escreveu:
> On Dec 15, 2011, at 11:58 AM, Rodrigo Rosenfeld Rosas wrote:
>
>> For instance, I wanted to know if this is possible:
>>
>> class MyViewRenderer<  DefaultRender # DefaultRender defines the 'render' method
>>   def list
>>     render_and_return[view: 'not_allowed'] unless allowed?
>>     render view: 'list' # shouldn't get called
>>   end
>>
>>   protected
>>
>>   def render_and_return
>>     proc {|*args| render *args; return }
>>   end
>>
>>   def allowed?
>>     false
>>   end
>> end
>>
>> Yeah, I know several folks will point me out that I should be using catch-throw for achieving something like this, but that is not the point.
> Catch-throw?  I remember reading about that but I'll bet it is one of the least used parts of Ruby.
>
> The return keyword in a block or in a simple proc (but not in a lambda) always returns from the method in which it is lexically embedded.  In your example this means that 'return' is associated with the method render_and_return.  If you invoke the proc returned by render_and_return, you get an error because the associated method is not active when the return is executed.
>
> Note:  This all assumes you are using Ruby 1.9.X where the proc method creates a simple proc and not a lambda.  If you try your example in Ruby 1.8.X you won't get an error since you'll be calling a lambda and 'return' in a lambda simply ends execution of the block (and *not* the enclosing method).

Yeah, I already knew about all this. What I'm asking is why Ruby won't 
allow me to return from another method, ie, passing procs between 
different methods that will allow me to return from any method through 
that proc...

I would like to know the reasoning behind this design decision... Is it 
just difficult technically to implement such behavior, or is it 
undesired? For the latter case, I would like to know why it is undesired...

>> What I'm saying is that I can't find any official reference about the meanings of "return", "next" or "break", for instance. Nor can I find the reason why such construction is not allowed.
> This is discussed in _The_Ruby_Programming_Language_ by David Flanagan and Matz, which I consider to be more in the style of a programming reference manual than the more commonly referenced Pickaxe book by Dave Thomas (_Programming_Ruby_).  I believe you are correct that there is no commonly referenced online 'official' language definition.  There is the following ISO draft:<http://www.ipa.go.jp/osc/english/ruby/Ruby_final_draft_enu_20100825.pdf>  but I don't think its description of 'return' is very helpful and the entire spec is out of date relative to language as defined by MRI 1.9.3.

Exactly, that was my point.

>> So, what is the reason why the proc with a return can only be called inside the method that created it?
> Why it behaves this way gets more into the philosophy of language design. With your proposed semantics it isn't possible to look at the source code and understand that the proc will cause a return from list() while the current semantics of 'return' do let you look at the source and know what method will terminate execution because of the return.

Humm... I think I get the point... For this case, if Ruby supported type 
verification, this situation could be avoided. For example, suppose a 
lambda is an instance of a different class (yes, I know it currently is 
a special Proc):

def my_method(b, callback#Lambda)
     # callback here can't be a proc so you can be sure that it won't 
return from your method
     # I'm using argument#type as a possible syntax for indicating the 
type checking made by the Ruby interpreter...
     # I'm also supposing that proc{} would create a Proc, while 
lambda{} would create a Lambda...
end

Yes, I agree that too much work would be necessary just for supporting 
this... I'm not actually suggesting that this should be supported, I'm 
only wondering how we could make this possible...

But I agree with you... I wouldn't like to debug a code that could have 
returned in the middle of a block without seeing a "return" there...

> The code you seem to want to avoid seems simple enough though.  Making an explicit return implicit doesn't seem like an improvement to me.  What is wrong with:
>
> def list
>    if allowed?
>       render view: 'list'
>    else
>       render view: 'not_allowed'
>    end
> end
>
> or
>
> def list
>    view = allowed? ? 'list' : 'not_allowed'
>    render view: view
> end

Ok, I admit that you couldn't have understood my reasoning by this small 
snippet. If you ask anyone doing real web development, they will tell 
you that it is a common idiom to do something like:

def action
     (render text: "failure while creating post"; return ) unless 
Post.create(params)
     expire_cache_from_post_list
     render view: 'list'
end

I'm being very generic here and not using any specific web framework but 
all of them are very similar with regards to this common use. Some of 
them won't allow more than a single render call, as it won't make sense 
to them and will raise a DoubleRenderException in the next render.

So, you'll get an exception if you forget to return after rendering. But 
usually, you'll always want to return after a render call because you're 
finished dealing with the request in 99.99% of the cases.

If returning from another method was possible, the framework could make 
the render method return automatically after being called, for example. 
That way, you would be able to simply write:

def action
     render text: "failure while creating post" unless Post.create(params)
     expire_cache_from_post_list
     render view: 'list'
end

And this is much more readable to me from the perspective of someone 
used to web programming. It would be implicit to the developer that 
either "return" or "render" would stop the method execution. In 
frameworks that don't support rendering by convention (you must call 
render explicitly) it wouldn't even make sense to see a return in such 
action methods.

I hope this example illustrates better the idea behind the desire for 
returning from other methods...

Cheers,

Rodrigo.