From: Robert Klemme Date: 2004-08-12T21:11:11+09:00 Subject: Re: [proto-rcr] Blocks: default arguments and method signatures "Sam McCall" schrieb im Newsbeitrag news:1092309945.876790@drone1-svc-skyt.qsi.net.nz... > I thought I'd post these ideas here, since last time I wrote up an RCR > and then got told that the issue had already been addressed in plans for > Ruby 2. Also, some ideas I'm pretty happy with, some are quite > radical/provocative. Hopefully those latter bits are somewhat > independent, which ones do you like? (if any ;) > So please let me know if I'm missing something, or this could be done > better. > > 1) Default arguments for &block > Often a method performs some simple reasonably useful behaviour if no > block is given, otherwise it lets the block do something more useful. > For example: > > def transform_values(array) > out=[] unless block_given? > array.each { |value| > # calculations... > if block_given? > yield value,newvalue > else > out<< newvalue > end > } > out > end Note: I typically do it like this: def transform_values(array) out = block_given? ? nil : [] array.each do |value| # calculations... if out out << newvalue else yield value, newvalue end end out or array end Although this version still has the drawback of retesting whether there is a block on each iteration. Another alternative is def transform_values(array, &b) unless b out = [] b = lambda {|o,n| out << n} end array.each do |value| # calculations... b.call value, newvalue end out or array end > I propose &block could take a default argument, probably of the form > &block={|x| foo}, but I could live with &block=proc {|x| foo}. > block_given? would return *false* if the default value was used (I'm > flexible on this bit). > The default block would be scoped *inside* the method. I like that idea, but block_given? but I don't like the return value for block_given?. OTOH, if you declare &b with a default parameter you'll always have a block and thus would never query. Addional note: you can achieve the same with b = lambda {|x| foo} unless b This idiom is a bit more powerful than the default block, because you can do arbitrary things (define variables) beforehand. > That example would become: > > def transform_values(array) &block={|val,newval| out<< newval} > out=[] unless block_given? > array.each { |value| > #calculations... > yield value,newvalue > } > end I doubt that this will work: because of the scoping 'out' cannot be known in the block: >> def t >> b = lambda {|x| out << x} >> out = [] >> b.call "foo" >> out >> end => nil >> t NameError: undefined local variable or method `out' for main:Object from (irb):2:in `t' from (irb):2:in `call' from (irb):4:in `t' from (irb):7 This would probably dramatically limit the usefulness of this suggestion. > 2) Taking anonymous block parameter, making it part of the signature > Two issues: > a) If block default values are adopted, then giving the block a > name might sometimes seem silly, as the name's never used. > See the above example, "block" is never referenced. > b) No way to specify taking a block as part of method signature. > Descrptive signatures are good: > * See syntax in auto-docs/code without reading whole thing > * See syntax in auto-docs/code when there's no comments > * Flag errors at start of method, and on every invocation. > > Proposal: allow "&" in place of "&block": > def foo & (or maybe: def foo &!) > As soon as the method is called, raises an error if no block is > given (similar to wrong number of args) > def foo &? (or maybe: def foo &) > No change from current behaviour of def foo, but denotes that > this method can take a block and might use it if given. > (MAYBE:) > def foo &block! or > def foo &!block or > replace current meaning(!) of: def foo &block > This method is required to take a block, not passing a block > raises an error. > def foo &block or > def foo &block? or > def foo &?block, etc > Current meaning of &block. I don't like this one because it's not much of an improvement. As far as I can see the only advantage is the automated block check on invocation. But that's here already: >> def t >> yield 1 >> end => nil >> t LocalJumpError: no block given from (irb):25:in `t' from (irb):27 from (null):0 Ok, if there is never a yield in the method, then there will be no error. So we gain a little more security. > My preferred syntax in a throw-everything-away-for-Ruby2 scenario: > Named block Anonymous block > Disallowed def foo def foo > Optional def foo &block? def foo &? > Defaulted def foo &block={} def foo &={} > Compulsory def foo &block def foo & > > My preferred syntax in a backwards-compatible scenario: > Named block Anonymous block > Disallowed - - > Optional def foo &block def foo &? > Defaulted def foo &block={} def foo &={} > Compulsory def foo &block! def foo &! Sorry, I don't understand these. Can you elaborate, please? Kind regards robert