[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Why does this use a block

Anonymous

10/11/2007 7:12:00 AM

So I'm new to Ruby and I'm trying to use OptionParser to parse in some
command line arguments. I looked at some documentation on OptionParser and
there is something that's confusing me to no end. In all the examples a
block is being passed in to the constructor of the OptionParser object. The
constructor simply makes a yield call, passing self as an argument.

What is the point of this? Can we not convert

opts = OptionParser.new() {|opts| body}

To

opts = OptionParser.new()
body

? This seems more natural to me. But the former seems like a common Ruby
idiom so I'm wondering what I am missing.

Thanks,

Jaimrk


12 Answers

Eric Hodel

10/11/2007 8:20:00 AM

0

On Oct 11, 2007, at 24:12 , Anonymous wrote:
> So I'm new to Ruby and I'm trying to use OptionParser to parse in some
> command line arguments. I looked at some documentation on
> OptionParser and there is something that's confusing me to no end.
> In all the examples a block is being passed in to the constructor
> of the OptionParser object. The constructor simply makes a yield
> call, passing self as an argument.
>
> What is the point of this? Can we not convert
>
> opts = OptionParser.new() {|opts| body}
>
> To
>
> opts = OptionParser.new()
> body
>
> ? This seems more natural to me. But the former seems like a common
> Ruby idiom so I'm wondering what I am missing.

It works both ways. Blocks are pretty, so people use them.

--
Poor workers blame their tools. Good workers build better tools. The
best workers get their tools to do the work for them. -- Syndicate Wars



Erik Veenstra

10/11/2007 9:28:00 AM

0

About using blocks in constructors in general...

(Although "initialize" is an initializer and not a constructor.
It only takes care of one part of the construction.)

"initialize" should take care of the initialization of the
object: Setting instance variables and checking whether the
instance variables are set correctly. It's the latter which is
important.

Setting instance variables is traditionally done in two ways: a
list of arguments and a hash of options. (Or a combination of
them.) You might want to call them "arguments" and "named
arguments".

The list:

def initialize(first_name, last_name)
@first_name = first_name.to_s
@last_name = last_name.to_s

raise ArgumentError, "first name isn't set" unless @first_name
raise ArgumentError, "last name isn't set" unless @last_name
end

The hash:

def initialize(options={})
@case_sensitive = options[:case_sensitive] || true
@direction = options[:direction] || :forward

raise ArgumentError, "" unless @first_name
end

In Java, you'll often see a third way (Java style, Ruby code):

attr_writer :first_name
attr_writer :last_name

def initialize
end

something = Something.new
something.first_name = "Erik"
something.last_name = "Veenstra"

Personally, I think this is conceptually unacceptable: There's
no way for the constructor to check whether the instance
variables are set, or whether they are set correctly.

This can be overcome by calling the initializer with a block:

attr_writer :first_name
attr_writer :last_name

def initialize(&block)
block.call(self)

raise ArgumentError, "first name isn't set" unless @first_name
raise ArgumentError, "last name isn't set" unless @last_name
end

Which is called like this:

Something.new do |something|
something.first_name = "Erik"
something.last_name = "Veenstra"
end

Personally, I always try to freeze the object once it is
created: (This has advantages beyond the scope of this writing.)

attr_writer :first_name
attr_writer :last_name

def initialize(&block)
block.call(self)

raise ArgumentError, "first name isn't set" unless @first_name
raise ArgumentError, "last name isn't set" unless @last_name

freeze
end

There's some more advantages. For example, the object can often
be collected earlier. Consider this example, in the main of the
application:

direction = :forward

EV::CommandLine.new do |cl|
cl.option("-r", "--reverse") {direction = :backward}
end.parse(ARGV)

...instead of...

direction = :forward
cl = EV::CommandLine.new
cl.option("-r", "--reverse") {direction = :backward}
cl.parse(ARGV)

When will cl be collected, in the second piece of code? Never?

It's just my opinion. Feel free to ignore me...

gegroet,
Erik V. - http://www.erikve...


Robert Klemme

10/11/2007 9:33:00 AM

0

2007/10/11, Eric Hodel <drbrain@segment7.net>:
> On Oct 11, 2007, at 24:12 , Anonymous wrote:
> > So I'm new to Ruby and I'm trying to use OptionParser to parse in some
> > command line arguments. I looked at some documentation on
> > OptionParser and there is something that's confusing me to no end.
> > In all the examples a block is being passed in to the constructor
> > of the OptionParser object. The constructor simply makes a yield
> > call, passing self as an argument.
> >
> > What is the point of this? Can we not convert
> >
> > opts = OptionParser.new() {|opts| body}
> >
> > To
> >
> > opts = OptionParser.new()
> > body
> >
> > ? This seems more natural to me. But the former seems like a common
> > Ruby idiom so I'm wondering what I am missing.
>
> It works both ways. Blocks are pretty, so people use them.

There are in fact important differences to both approaches. First,
OptionParser's #initialize is free to yield whatever it decides to the
block. So you are not guaranteed to receive the new OptionParser
instance. This is important to keep in mind because the
implementation might change at a later point so the lib could yield a
different instance which should, of course, support the documented
interface.

Second, with the block form OptionParser#initialize is able to detect
when the user initialization has finished and can do finalizing work.
It could, for example, invoke #freeze to make sure the configuration
is not changed any more. Or it can verify that client code properly
initialized it (avoiding duplicate or ambiguous option settings for
example).

An alternative that some may find nicer would implement OptionParser()
(a method) in Kernel and have that receive the block.

Kind regards

robert

Florian Frank

10/11/2007 9:34:00 AM

0

Anonymous wrote:
> So I'm new to Ruby and I'm trying to use OptionParser to parse in some
> command line arguments. I looked at some documentation on OptionParser and
> there is something that's confusing me to no end. In all the examples a
> block is being passed in to the constructor of the OptionParser object. The
> constructor simply makes a yield call, passing self as an argument.
>
> What is the point of this? Can we not convert
>
> opts = OptionParser.new() {|opts| body}
>
> To
>
> opts = OptionParser.new()
> body
>
> ? This seems more natural to me. But the former seems like a common Ruby
> idiom so I'm wondering what I am missing.
>
You might want to use the shortcut

option_parser = OptionParser.new do |o|
o.foo = "foo"
o.bar = "bar"
...
end

during initialization of the object instead of using the long name, that
will be used later in the program after you forgot, what "o" meant.

I don't know if this is the case with OptionParser, but many methods
also want to do some housekeeping after the execution of the block, for
example, open(...) do |f| ... end closes the opened file. A constructor
might want to check his configuration (given in the block) and raise an
exception, if errors were found. This is better than creating a wrongly
configured object, that only will throw an exception later in the program.

--
Florian Frank


Todd Benson

10/11/2007 10:44:00 AM

0

On 10/11/07, Anonymous <jaimrk@aol.com> wrote:
> So I'm new to Ruby and I'm trying to use OptionParser to parse in some
> command line arguments. I looked at some documentation on OptionParser and
> there is something that's confusing me to no end. In all the examples a
> block is being passed in to the constructor of the OptionParser object. The
> constructor simply makes a yield call, passing self as an argument.
>
> What is the point of this? Can we not convert
>
> opts = OptionParser.new() {|opts| body}
>
> To
>
> opts = OptionParser.new()
> body
>
> ? This seems more natural to me. But the former seems like a common Ruby
> idiom so I'm wondering what I am missing.
>
> Thanks,
>
> Jaimrk

You are asking a bunch of questions here, but I'll try my hand at one of them.

yield self

It pretty much does exactly what the statement says it does. Why
would you do this upon initializing an object? I suppose, because you
might want something else to accomplish something during
initialization.

Blocks also give you control of scope and give the programmer a sense
of finalization. File#open for example.

Todd

Rick DeNatale

10/11/2007 12:56:00 PM

0

On 10/11/07, Robert Klemme <shortcutter@googlemail.com> wrote:
> 2007/10/11, Eric Hodel <drbrain@segment7.net>:
> > On Oct 11, 2007, at 24:12 , Anonymous wrote:
> > > So I'm new to Ruby and I'm trying to use OptionParser to parse in some
> > > command line arguments. I looked at some documentation on
> > > OptionParser and there is something that's confusing me to no end.
> > > In all the examples a block is being passed in to the constructor
> > > of the OptionParser object.

> > It works both ways. Blocks are pretty, so people use them.
>
> There are in fact important differences to both approaches. First,
> OptionParser's #initialize is free to yield whatever it decides to the
> block. So you are not guaranteed to receive the new OptionParser
> instance. This is important to keep in mind because the
> implementation might change at a later point so the lib could yield a
> different instance which should, of course, support the documented
> interface.
>
> Second, with the block form OptionParser#initialize is able to detect
> when the user initialization has finished and can do finalizing work.
> It could, for example, invoke #freeze to make sure the configuration
> is not changed any more. Or it can verify that client code properly
> initialized it (avoiding duplicate or ambiguous option settings for
> example).

I'm pretty sure that I've seen a variant on this in which initialize
evaluates the block with instance_eval rather than yield or call.
This allows the block to invoke private methods during initialization.

IIRC the Ruby tk bindings so this.

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denh...

7stud 7stud

10/11/2007 1:17:00 PM

0

Anonymous wrote:
> So I'm new to Ruby and I'm trying to use OptionParser to parse in some
> command line arguments.

In ruby, a lot of methods can be called with or without a block. When a
method is called with a block, it may do some extra stuff. I think what
the other posters are saying is that the initialize() method in
OptionParser, which is called when you call new(), may be defined to do
some extra things when it's called with a block.

Here's an example of a method doing some extra things when it's called
with a block:

class Dog
def age
@age
end

def age=(num)
@age = num
end

def initialize
if block_given?
yield self

if @age == nil
puts "You must set the dog's age."
elsif @age > 20
puts "Error. That dog is dead."
elsif @age < 0
puts "Error. That dog isn't born yet."
end

end
end
end



d = Dog.new
d.age = -3
puts "That dog's age is #{d.age}."
d.age = 22
puts "That dog's age is #{d.age}."

d = Dog.new do |dog|
dog.age = 22
end

--output:--
That dog's age is -3.
That dog's age is 22.
Error. That dog is dead.
--
Posted via http://www.ruby-....

Brian Adkins

10/11/2007 4:17:00 PM

0

On Oct 11, 5:27 am, Erik Veenstra <erikv...@gmail.com> wrote:
> Personally, I always try to freeze the object once it is
> created: (This has advantages beyond the scope of this writing.)

So, most of your objects are immutable?

Erik Veenstra

10/11/2007 7:34:00 PM

0

> So, most of your objects are immutable?

Yes. (If possible...)

Q: Why?
A: http://lambda-the-ultimate.org...

gegroet,
Erik V. - http://www.erikve...


Brian Adkins

10/11/2007 8:39:00 PM

0

On Oct 11, 3:33 pm, Erik Veenstra <erikv...@gmail.com> wrote:
> > So, most of your objects are immutable?
>
> Yes. (If possible...)
>
> Q: Why?
> A:http://lambda-the-ultimate.org...

Have you thought of programming in Haskell? ;)

I'm familiar with some of the benefits of avoiding state with respect
to functional programming languages, but to impose that strictly on
Ruby seems to be working against the grain. I do code many functions
(i.e. not associated with a meaningful instance) in a functional style
w/o side effects, but when working with objects, having methods modify
the state of objects seems to be very natural and productive in Ruby.

I'm curious though because of my recent research into Haskell, SML,
etc.. Do you have an example of this style of programming (frozen
objects) in Ruby that you feel is compelling?

Brian Adkins

--
http://www...
http://lojic...