[proto-rcr] Blocks: default arguments and method signatures

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 :wink:
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

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

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.

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 &!

Thoughts?
Sam

"Sam McCall" <tunah.usenet@tunah.net> 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 :wink:
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

Hi --

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 :wink:
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

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.
That example would become:

def transform_values(array) &block={|val,newval| out<< newval}

I can definitely see the point of the default assignment, but I
think you're changing too much here by putting it outside the
arglist. &block is a special argument, I know, but it's still
part of the list.

I also don't like the forward reference to a local variable here
(out). When you get to this:

  out= unless block_given?

it really feels a bit convoluted.

  array.each { |value|
    #calculations...
    yield value,newvalue

I think block.call(value,newvalue) would be clearer here, since yield
really means that the business is being done by switching control to a
block provided in association with the method call. I understand that
you're sort of retro-fitting your default block to play that role, but
I still think it's better to save yield for the traditional yield
scenario.

  }
end

You're not returning 'out'? And if you are, are you sure you want
the return value to be nil if a block was given?

Just for completeness, here's a present-day version that returns the
original array if a block is given, and otherwise returns the
accumulated transformations:

  def transform_values(array,&block)
    res = if block then array else end
    block ||= lambda {|val,newval| res << newval }

    array.each do |value|
      newvalue = value * 10
      block.call(value, newvalue)
    end

    return res
  end

  # A couple of test runs:

  p transform_values([1,2,3])
  p transform_values([1,2,3]) {|x,y| puts "#{x} => #{y}" }

  # Output

  [10, 20, 30]
  1 => 10
  2 => 20
  3 => 30
  [1, 2, 3]

David

路路路

On Thu, 12 Aug 2004, Sam McCall wrote:

--
David A. Black
dblack@wobblini.net

Since most of the problems in here have been hashed over already by those more competent than me, I'll just mention one thing that caught my eye:

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

To me, that code should make a new variable, named 'block'. How about this:

   def transform_values(array) {|val,newval| (out||=)<< newval}
     out= unless block_given?
     array.each { |value|
       #calculations...
       yield value,newvalue
     }
   end

(note that or-equaling 'out' solves the forward reference problem, at the cost of some visual cleanliness)

Basically, an unassigned block at the beginning of a method declaration would be taken as the yield block.

Another possibility would be to give the block it's own binding, as if it was defined in an intermediate method. That way, you wouldn't ever have to worry about namespace clashes.

cheers,
Mark

路路路

On Aug 12, 2004, at 4:26 AM, Sam McCall wrote:

Robert Klemme wrote:

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.

Thus the reason block_given? should return false when the default value was taken: it might as well, as the other sense is entirely useless :wink:
It would be needed to detect whether to do some setup for the default block (for non-default blocks this is done in the scope of the caller).
Of course this doesn't work as you point out.

Addional note: you can achieve the same with
b = lambda {|x| foo} unless b

Yeah. It seems a little more expressive/consistent/natural to have it as a default variable (for the same reasons we have defaults rather than optionals in ruby), but that probably is nice enough.

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:

Erk. You're right. I did test it - in irb, having used the name "out" a few pages up :wink: Seems like a showstopper.

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.

Improvements as I see them (excluding default-block-args stuff):
People reading the documentation/code can see the syntax without reading the whole description/method body. This is important to me, maybe not to others. Might be partially soluble by getting rdoc to look for yields.
Detect errors sooner. My biggest problem with Ruby is errors that aren't flagged till runtime. In scripts where there's 5 minutes of startup time, this makes debugging SLOW. Fix a bug, wait 5 minutes, fix another bug, wait 5 minutes...
Detect improbable errors. A method processes some data and takes a block which handles erroneous data in some customised way. Not passing a block doesn't cause an error if all you data is valid.

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?

Er. My mailer showed tabs as 8 spaces, and then expanded them to 4, or something :-\ It was *meant* to be a table showing how you would define a no-args function called foo that took no block/optional block/optional block with default/compulsory block, which would be named/anonymous.
My (updated) preferred syntax overall (ignoring compatibility):
Disallowed block: def foo
Optional anonymous block: def foo &=nil
Optional named block: def foo &block=nil
Compulsory anonymous block: def foo &
Compulsory named block: def foo &block

[Very simple default args (the default must be a proc object, block_given? is just "not block.nil?") could be unintrusively done, and would probably only be useful for the =nil syntax, and for completeness. I think it's worth it ;-)]

My (updated) preferred syntax overall (backwards compatible):
Disallowed block: <not possible>
Optional anonymous block: def foo OR: def foo &
Optional named block: def foo &block
Compulsory anonymous block: def foo &!
Compulsory named block: def foo &block!

Thanks for the input, I knew I would have missed something :slight_smile:
Sam

David A. Black wrote:

That example would become:

def transform_values(array) &block={|val,newval| out<< newval}

I can definitely see the point of the default assignment, but I
think you're changing too much here by putting it outside the
arglist. &block is a special argument, I know, but it's still
part of the list.

That was a typo :-#

I also don't like the forward reference to a local variable here
(out). When you get to this:

out= unless block_given?

it really feels a bit convoluted.

Yes, as I posted above, I tested the equivalent code but made a mistake. I withdraw this part :slight_smile:

array.each { |value|
  #calculations...
  yield value,newvalue

I think block.call(value,newvalue) would be clearer here, since yield
really means that the business is being done by switching control to a
block provided in association with the method call. I understand that
you're sort of retro-fitting your default block to play that role, but
I still think it's better to save yield for the traditional yield
scenario.

Or the block could be anonymous, which might be clear enough...

You're not returning 'out'?

Oops, I forgot. I really couldn't test this one!

And if you are, are you sure you want
the return value to be nil if a block was given?

That was the intention in this example. (Recently I've been using large data sets, one huge array is enough!)

Just for completeness, here's a present-day version that returns the
original array if a block is given, and otherwise returns the
accumulated transformations:

  def transform_values(array,&block)
    res = if block then array else end
    block ||= lambda {|val,newval| res << newval }

    array.each do |value|
      newvalue = value * 10
      block.call(value, newvalue)
    end

    return res
  end

Nice, I like the res trick...
For that matter:

def transform_values(array,&block)
    array.map{|value|
       newvalue=value*10
       block ? begin
         block[value,newvalue]; value
       end : newvalue
    }
end

Thanks,
Sam

"Sam McCall" <tunah.usenet@tunah.net> schrieb im Newsbeitrag
news:1092318554.586276@drone1-svc-skyt.qsi.net.nz...

Robert Klemme wrote:
Erk. You're right. I did test it - in irb, having used the name "out" a
few pages up :wink: Seems like a showstopper.

Unfortunately so...

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

Improvements as I see them (excluding default-block-args stuff):
People reading the documentation/code can see the syntax without reading
the whole description/method body. This is important to me, maybe not to
others.

Good point. I like things that improve doc - especially if it's done
automatically. :slight_smile:

Might be partially soluble by getting rdoc to look for yields.
Detect errors sooner. My biggest problem with Ruby is errors that aren't
flagged till runtime. In scripts where there's 5 minutes of startup
time, this makes debugging SLOW. Fix a bug, wait 5 minutes, fix another
bug, wait 5 minutes...

You might want to rethink your test scenario if that's possible.

Detect improbable errors. A method processes some data and takes a block
which handles erroneous data in some customised way. Not passing a block
doesn't cause an error if all you data is valid.

Same for methods that not not always yield (in case of an empty collection
for example). I often do a raise as first line if there is no block.

>>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?
Er. My mailer showed tabs as 8 spaces, and then expanded them to 4, or
something :-\

Ah, I wondered already why you didn't indent your code. Then that was
probably the mailer...

It was *meant* to be a table showing how you would define
a no-args function called foo that took no block/optional block/optional
block with default/compulsory block, which would be named/anonymous.
My (updated) preferred syntax overall (ignoring compatibility):

(1) > Disallowed block: def foo
(2) > Optional anonymous block: def foo &=nil
(3) > Optional named block: def foo &block=nil
(4) > Compulsory anonymous block: def foo &
(5) > Compulsory named block: def foo &block

IMHO (1) and (5) would break too much code. (2) and (4) necessitate a
parser change. Matz seems to be reluctant to do these because the parser
seems to be quite convoluted already, which in turn is caused by yacc
AFAIR...

[Very simple default args (the default must be a proc object,
block_given? is just "not block.nil?") could be unintrusively done, and
would probably only be useful for the =nil syntax, and for completeness.
I think it's worth it ;-)]

I'm not convinced.

My (updated) preferred syntax overall (backwards compatible):
Disallowed block: <not possible>

.... as it is today. Do you really suggest to change it in Ruby 1.x and
change it back in Ruby 2?

Optional anonymous block: def foo OR: def foo &
Optional named block: def foo &block
Compulsory anonymous block: def foo &!
Compulsory named block: def foo &block!

Thanks for the input, I knew I would have missed something :slight_smile:

You're welome. We all do once in a while.

Kind regards

    robert