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

ruby

    Next

  • 1. Job Vacancy RoR
    I am part of a small Agency that supplies contractors to clients. A fairly new client in San Francisco (SOMA), needs an experienced (at least one complete commercial project) Ruby on Rails Software Engineer. Additionally, they are using open source development environment, Linux, Apache, MySQL and Perl / PHP. This is a contract position for 3 to 6 months. You need to be onsite and will be part of a development Team. Must be eligible to work in USA, and for this particular contract must be local to San Francisco Bay Area. Please refer this to anyone that you know who is not a member and may not see it. Also, we are very interested in new clients in need of RoR developers. This is an exciting area and there are opportunities for everyone. Please contact me by e-mail (Stephen AT chancellor DOT com) or call Anthony at 415-332-0123. Thank you, Stephen
  • 2. change from int 1 on int 01
    I need to change variable as int = 1 on = 01 wrrrrwrrrrrrrrr

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

Postby Sam McCall » Fri, 13 Aug 2004 20:30:32 GMT

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

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

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

Postby Robert Klemme » Fri, 13 Aug 2004 21:07:47 GMT

"Sam McCall" < XXXX@XXXXX.COM > schrieb im Newsbeitrag
news: XXXX@XXXXX.COM ...

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


I doubt that this will work: because of the scoping 'out' cannot be known
in the block:

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


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:

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


Sorry, I don't understand these. Can you elaborate, please?

Kind regards

robert


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

Postby David A. Black » Fri, 13 Aug 2004 21:46:45 GMT

Hi --





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:


it really feels a bit convoluted.  


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.


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

-- 
David A. Black
 XXXX@XXXXX.COM 




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

Postby Sam McCall » Fri, 13 Aug 2004 22:54:06 GMT

obert Klemme wrote:
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 ;)
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.

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.

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

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.

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 :-)
Sam

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

Postby Sam McCall » Fri, 13 Aug 2004 23:29:59 GMT


That was a typo :-#

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

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

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

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

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

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

Postby Robert Klemme » Fri, 13 Aug 2004 23:49:45 GMT

"Sam McCall" < XXXX@XXXXX.COM > schrieb im Newsbeitrag
news: XXXX@XXXXX.COM ...

Unfortunately so...

as
invocation.

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


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


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.


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

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


I'm not convinced.


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


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

Kind regards

robert


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

Postby Mark Hubbart » Sat, 14 Aug 2004 02:29:55 GMT

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:





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




Similar Threads:

1.A block is an inside-out method [Was: info on block arguments]

> No. sort_by is taking the block and for each item in the array, it puts
> that item into |toy|, and then grabs the :shape from it.

A block is an everted (inside-out) method.

A normal function:

  def foo(x)
    x + 42
  end

A similar block:

  y = 42
  block_user do |x|
    x + y
  end

The call site for foo would just look like foo(12). The name of foo is on 
the outside, its calling parens are on the outside, and its behavior is 
on the inside.

The block turns this pattern inside out. The call site (yield(12) or 
block.call(12)) is inside the block_user.

The behavior is on the outside.

So when foo takes its argument, it needs delimiters to name the argument 
for the interpreter. (x).

When block_user passes x, the block needs delimiters to name its 
argument, so its block body can use it. |x|.

And because the behavior is on the outside, it can share variables from 
its enclosing scope. foo(), by contrast, can only share variables from 
its enclosing class.

-- 
  Phlip

2.Default argument values for blocks

Is there a reason why I can't do this?

   foo = lambda { |foo = bar| puts foo }
   foo.call

I can't think of any good reason why this isn't valid.


Cheers,
Daniel Schierbeck

3.named method arguments with defaults

Hello all,

What is the current idiomatic approach to pass named arguments to a
method in Ruby ?

In Perl, I can do:

sub method
{
  # first the defaults are specified, then @_ is appended and overrides
  # the defaults, using Perl's array-folding-into-hash feature
  #
  my %args = ("size" => 45,
                      "length" => 12,
                      @_);

  my $the_size = $args{"size"};
}

# now, a call: 44 will override the default size 45, but length will
remain 12
#
method("size" => 44);

I'm sure Ruby has a nearly similar approach to achieve this, I just not
skilled enough (yet) to know how. Please advise.

Thanks in advance
Eli

4.Defining a method with default arguments in C

Brian Takita wrote:
> Hello,
>
> How can I define a default argument value such as the following in c?
>
> def foobar(arg=1)
> end
>
> Thanks,
> Brian Takita
>
Specify the number of arguments as -1 in rb_define_method:

rb_define_method(myclass, "method_name", meth_fnc, -1);

In your method use rb_scan_args to scan the argument list. If you don't 
get enough arguments, use the default values instead.

5.Defining a method with an argument with a default value

I have this line in a class method to define an initializer:

define_method(:initialize){|value| @value = value}

But I want something more like this:

define_method(:initialize){|value=nil| @value = value if value}

which doesn't seem to be possible because of the "value=nil" part. Is
there some way to do this that isn't eval? Eval would work fine since
this is pretty static code and I have unit tests for it but I try to
avoid it if I can. I've only used it before for performance to turn
some code with runtime tests into eval-time tests.

Pedro.

6. Default values for method arguments

7. default passed block for method

8. Methods with [] braces can't take block arguments?



Return to ruby

 

Who is online

Users browsing this forum: No registered users and 2 guest