Keyword arguments?

[snip]

Keyword arguments help with ambiguity.
[snip]

this is close

class Person
def initialize args
@name = args[:name]
@age = args[:age]
end
end

me = Person.new :name => ‘ara’, :age => 30

note that’s a hash, not keywords - but you get the same effect.

hope that helps.

-a

···

On Tue, 12 Nov 2002, Vis Mike wrote:

====================================

Ara Howard
NOAA Forecast Systems Laboratory
Information and Technology Services
Data Systems Group
R/FST 325 Broadway
Boulder, CO 80305-3328
Email: ahoward@fsl.noaa.gov
Phone: 303-497-7238
Fax: 303-497-7259
====================================

Dave & Andy also noted that you can make it so that initialize
receives a block. Lyle recently implemented a change to the FXRuby
code that makes the technique cleaner (in that you don’t have to use
‘self’ all over the place), like so:

class Person
attr_accessor :name, :age

def initialize(name = nil, age = nil)
@name = name
@age = age
# … default initialization
yield self if block_given?
end
end

me = Person.new { |who|
who.name = ‘austin’
who.age = 31
}

-austin
– Austin Ziegler, austin@halostatue.ca on 2002.11.13 at 09.59.51

···

On Wed, 13 Nov 2002 19:17:14 +0900, ahoward wrote:

On Tue, 12 Nov 2002, Vis Mike wrote:

Keyword arguments help with ambiguity.
this is close

class Person
def initialize args
@name = args[:name]
@age = args[:age]
end
end

me = Person.new :name => ‘ara’, :age => 30

note that’s a hash, not keywords - but you get the same effect.

[snip]

Keyword arguments help with ambiguity.
[snip]

this is close

class Person
def initialize args
@name = args[:name]
@age = args[:age]
end
end

me = Person.new :name => ‘ara’, :age => 30

Amidst all the discussion of various ways of achieving keyword arguments or
similar, one really nice feature has been overlooked: easy default arguments.
Here is a representation of code I’ve written recently.

class Recorder
def initialize(init_params={})
init = process_params(init_params)
@database = init[:DATABASE]
@user = init[:USER]
@password = init[:PASSWORD]
end

def Recorder.init_defaults
{
:DATABASE = “…”,
:USER = “…”,
:PASSWORD = “…”,
}
end

def process_params(hash)
defaults = init_defaults()
# Guard user against invalid keys
hash.keys.each do |key|
raise “Key not supported: #{key}” unless defaults.has? key
end
defaults.update(hash)
end
end

Now,
Recorder.new and
Recorder.new(:USER => “smith”, :PASSWORD => “apple”)
will use the appropriate defaults, and
Recorder.new(:LOG_FILE => “x.log”)
will fail with a meaningful error message.

Further, if anyone wants to find out the defaults, they only have to ask:
Recorder.init_defaults

I’ve been very happy with this, and see no need for Ruby to adopt explicit
keyword arguments.

Gavin

···

From: “ahoward” ahoward@fsl.noaa.gov

On Tue, 12 Nov 2002, Vis Mike wrote:

Austin Ziegler wrote:

Dave & Andy also noted that you can make it so that initialize
receives a block. Lyle recently implemented a change to the FXRuby
code that makes the technique cleaner (in that you don’t have to use
‘self’ all over the place), like so:

class Person
attr_accessor :name, :age

def initialize(name = nil, age = nil)
@name = name
@age = age
# … default initialization
yield self if block_given?
end
end

me = Person.new { |who|
who.name = ‘austin’
who.age = 31
}

Cleaner, maybe, but there is a tradeoff. Both @name and @age must have
writers for the above to work. With the other approach (block is handled
with instance_eval), you can write:

me = Person.new {
@name = ‘austin’
@age = 31
}

Either way, you’re breaking encapsulation–by exposing writers, or by
exposing the attrs themselves at the time of construction (only).

Austin Ziegler wrote:

Dave & Andy also noted that you can make it so that initialize
receives a block. Lyle recently implemented a change to the
FXRuby code that makes the technique cleaner (in that you don’t
have to use ‘self’ all over the place), like so:

[ruby-talk:55739]

Cleaner, maybe, but there is a tradeoff. Both @name and @age must
have writers for the above to work. With the other approach (block
is handled with instance_eval), you can write:

Either way, you’re breaking encapsulation–by exposing writers, or
by exposing the attrs themselves at the time of construction
(only).

You’re right, and in some cases, that’s OK (see Text::Format for
this; I haven’t yet modified it to use Lyle’s change – I may not),
since all of the methods are exposed anyway.

Alternatively, using a semi-combination of the two methods, you can
use otherwise private methods (see MIME::Types::Type for an example
of this). (Interestingly, it does NOT seem to work with
attr_accessor, but if I manually define name= as private, it works.)

Text::Format has an initializer which can act as a copy constructor
(accepting another Text::Format), with a Hash (using string names,
not symbols; that’s not too hard a change to make, though), a
String, and no argument – and all forms accept a block for further
customization. Text::Format by default exposes the @varname form; it
will probably be changed to allow what Lyle does.

MIME::Types::Type has an initializer which can act as a copy
constructor, with an Array, a a Hash, or a String. It too, accept
blocks. It doesn’t allow assignment after creation, so it has some
private functions that can only be called from within the
constructor (and it’s documented as such).

I think I may submit an RCR for instance_eval to be modified such
that there is a way to specify what can/will be yielded to the block
given.

-austin
– Austin Ziegler, austin@halostatue.ca on 2002.11.13 at 15.24.26

···

On Thu, 14 Nov 2002 05:10:40 +0900, Joel VanderWerf wrote:

Austin Ziegler wrote:

Either way, you’re breaking encapsulation–by exposing writers, or
by exposing the attrs themselves at the time of construction
(only).

You’re right, and in some cases, that’s OK (see Text::Format for
this; I haven’t yet modified it to use Lyle’s change – I may not),
since all of the methods are exposed anyway.

The original implementation (introduced in FXRuby 1.0.14) used
instance_eval to evaluate the initialization block. I changed it in
FXRuby 1.0.15 to instead yield ‘self’ to the block, primarily because
(as Rich Kilmer so eloquently noted “instance_eval() is evil”.

I sort-of agree with Joel’s point that either approach breaks
encapsulation, assuming that the class’ public interface doesn’t already
provide writers for the attributes of interest. But I think of the two,
using instance_eval to explicitly open-up instance variables is
definitely more brittle. Remember, an instance method that is a “plain
old” attr_writer today could be replaced in the future by a different
implementation that does something different (i.e. touches different
instance variables or whatever).

···

On Thu, 14 Nov 2002 05:10:40 +0900, Joel VanderWerf wrote:

Lyle Johnson wrote:

Austin Ziegler wrote:

Either way, you’re breaking encapsulation–by exposing writers, or
by exposing the attrs themselves at the time of construction
(only).

You’re right, and in some cases, that’s OK (see Text::Format for
this; I haven’t yet modified it to use Lyle’s change – I may not),
since all of the methods are exposed anyway.

The original implementation (introduced in FXRuby 1.0.14) used
instance_eval to evaluate the initialization block. I changed it in
FXRuby 1.0.15 to instead yield ‘self’ to the block, primarily because
(as Rich Kilmer so eloquently noted “instance_eval() is evil”.

I sort-of agree with Joel’s point that either approach breaks
encapsulation, assuming that the class’ public interface doesn’t already
provide writers for the attributes of interest. But I think of the two,
using instance_eval to explicitly open-up instance variables is
definitely more brittle. Remember, an instance method that is a “plain
old” attr_writer today could be replaced in the future by a different
implementation that does something different (i.e. touches different
instance variables or whatever).

I agree completely. There’s also the point that using yield rather than
instance_eval lets you refer to attributes in the static scope:

@last_name = “powers”

me = Person.new {
@name = ‘austin’ + @last_name
@age = 31
}

With instance_eval, you have to pass @last_name into the block using a
local var.

···

On Thu, 14 Nov 2002 05:10:40 +0900, Joel VanderWerf wrote:

Lyle Johnson wrote:

Austin Ziegler wrote:

Either way, you’re breaking encapsulation–by exposing writers, or
by exposing the attrs themselves at the time of construction
(only).

You’re right, and in some cases, that’s OK (see Text::Format for
this; I haven’t yet modified it to use Lyle’s change – I may not),
since all of the methods are exposed anyway.

The original implementation (introduced in FXRuby 1.0.14) used
instance_eval to evaluate the initialization block. I changed it in
FXRuby 1.0.15 to instead yield ‘self’ to the block, primarily because
(as Rich Kilmer so eloquently noted “instance_eval() is evil”.

I sort-of agree with Joel’s point that either approach breaks
encapsulation, assuming that the class’ public interface doesn’t already
provide writers for the attributes of interest. But I think of the two,
using instance_eval to explicitly open-up instance variables is
definitely more brittle. Remember, an instance method that is a “plain
old” attr_writer today could be replaced in the future by a different
implementation that does something different (i.e. touches different
instance variables or whatever).

I agree completely. There’s also the point that using yield rather than
instance_eval lets you refer to attributes in the static scope:

@last_name = “powers”

me = Person.new {
@name = ‘austin’ + @last_name
@age = 31
}

With instance_eval, you have to pass @last_name into the block using a
local var.

···

On Thu, 14 Nov 2002 05:10:40 +0900, Joel VanderWerf wrote:

Austin Ziegler wrote:

Either way, you’re breaking encapsulation–by exposing writers,
or by exposing the attrs themselves at the time of construction
(only).
You’re right, and in some cases, that’s OK (see Text::Format for
this; I haven’t yet modified it to use Lyle’s change – I may
not), since all of the methods are exposed anyway.
The original implementation (introduced in FXRuby 1.0.14) used
instance_eval to evaluate the initialization block. I changed it
in FXRuby 1.0.15 to instead yield ‘self’ to the block, primarily
because (as Rich Kilmer so eloquently noted “instance_eval() is
evil”.

Well, I’m not sure that I’d agree tht it’s “evil”, but it is most
certainly “ugly.” (I consider most eval situations “ugly”, including
one that I’ll probably have to do because I can’t find a way other
than “eval” to “compile” a string into a proc.)

It would still be useful to yield-with-context so that I can use
either protected or private methods within a block; something like:

instance_yield args if block_given?

I sort-of agree with Joel’s point that either approach breaks
encapsulation, assuming that the class’ public interface doesn’t
already provide writers for the attributes of interest. But I
think of the two, using instance_eval to explicitly open-up
instance variables is definitely more brittle. Remember, an
instance method that is a “plain old” attr_writer today could be
replaced in the future by a different implementation that does
something different (i.e. touches different instance variables or
whatever).

I actually completely agree with Joel’s point. The only reason that
Text::Format doesn’t use your method is I didn’t think of it (:

-austin
– Austin Ziegler, austin@halostatue.ca on 2002.11.13 at 18.18.20

···

On Thu, 14 Nov 2002 06:58:16 +0900, Lyle Johnson wrote:

On Thu, 14 Nov 2002 05:10:40 +0900, Joel VanderWerf wrote:

right…it looks more like this with yield:

@last_name = “powers”

Person.new do |person|
person.name = ‘austin’+@last_name
person.age = 32
end

For FXRuby this is really handy because you don’t need to keep
references to the created objects (like buttons, etc) do the yield thing
works great.

-rich

···

-----Original Message-----
From: Joel VanderWerf [mailto:vjoel@PATH.Berkeley.EDU]
Sent: Wednesday, November 13, 2002 5:11 PM
To: ruby-talk ML
Subject: Re: Keyword arguments?

I agree completely. There’s also the point that using yield
rather than instance_eval lets you refer to attributes in the
static scope:

@last_name = “powers”

me = Person.new {
@name = ‘austin’ + @last_name
@age = 31
}

With instance_eval, you have to pass @last_name into the
block using a local var.

Joel VanderWerf wrote:

@last_name = “powers”

me = Person.new {
@name = ‘austin’ + @last_name
@age = 31
}

Oops, should have been

me = Person.new { |who|
who.name = ‘austin’ + @last_name
who.age = 31
}