Argument Passing Syntax

Why are arguments to the '' method parsed differently
than a standard method?

class A
   def m1(*args)
     puts args.inspect
   end
   def (*args)
     puts args.inspect
   end
end

a = A.new

=> #<A:0x321c28>

a.m1(1,2)

[1, 2]
=> nil

a.m1(1 => 2)

[{1=>2}]
=> nil

a.m1(1, 2 => 3)

[1, {2=>3}]
=> nil

a[1,2]

[1, 2]
=> nil

a[1 => 2]

[{1=>2}]
=> nil

a[1, 2 => 3]

SyntaxError: compile error
(irb):15: syntax error
a[1, 2 => 3]
          ^
         from (irb):15

Gary Wright

I haven't seen that syntax error before. I don't see any reason why
that shouldn't parse OK - I'd call it a bug in the parser, unless Matz
says otherwise.

It's interesting because it should (IMHO) be possible to pass anonymous
and named parameters when calling a Proc, like so:

check["6 times 9", :answer => 42]

Cheers,
Dave

Why are arguments to the '' method parsed differently
than a standard method?

[snip]

> a.m1(1, 2 => 3)
[1, {2=>3}]
=> nil

uses call_args (and friends)

> a[1 => 2]
[{1=>2}]
=> nil

uses assoc_list, for Hash::

> a[1, 2 => 3]
SyntaxError: compile error
(irb):15: syntax error
a[1, 2 => 3]
         ^
        from (irb):15

I imagine the second syntax was added only for Hash::.

···

On Oct 27, 2005, at 12:31 PM, gwtmp01@mac.com wrote:

--
Eric Hodel - drbrain@segment7.net - http://segment7.net
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04

Because () and are parsed differently. is primarily an array
reference (aref).
You can't pass blocks either in 1.8. In 1.9 you will be able to do this:

class O
  def (*args, &block)
    yield(args) if block_given?
  end
end

o = O.new
o.(1,2,3) { |x| p x } # this is what you have to do in 1.8
o[1,2,3] { |x| p x } # this causes syntax error in 1.8 but works in 1.9

but not this (as far as I know):

o[1,:var => 3] # syntax error in 1.8 & 1.9

Regards,

Sean

···

On 10/27/05, gwtmp01@mac.com <gwtmp01@mac.com> wrote:

Why are arguments to the '' method parsed differently
than a standard method?

Why are arguments to the '' method parsed differently
than a standard method?

Because () and are parsed differently. is primarily an array
reference (aref).

But *why* are they parsed differently? Just "because"?

For example, the common technique of aliasing : to :new means you
can't expect to have ruby parse a trailing hash in the argument list
when the method is called via : but you can when called via :new.

I understand that the : method syntax has a different evolutionary
path than the standard method syntax but is that the only reason that
the arguments are parsed differently?

You can't pass blocks either in 1.8. In 1.9 you will be able to do this:
o[1,2,3] { |x| p x } # this causes syntax error in 1.8 but works in 1.9

Makes sense. I vote to extend the change to allow:

o[1,2,3,:key => val]

Note, you can already have an implicit literal hash, you just can't precede
it with other arguments.

o[:key => val] # OK now.
o[1,2,3] # OK now.
o[1,2,3,:key=>val] # bad now.

Gary Wright

···

On Oct 28, 2005, at 5:24 AM, Sean O'Halpin wrote:

On 10/27/05, gwtmp01@mac.com <gwtmp01@mac.com> wrote:

Here is a related thought.

Right now you can't have arguments to an "assignment method":

class A
def attribute1=(*args, &block)
   @attr1 = value
end

a = A.new
a.attribute1 = 4 # ok
a.attribute1(4) = 5 # syntax error
a.attribute1=(4, 5) # syntax error
a.send(:attribute1=, 4,5) # ok

Why not allow the middle two cases? I think this would be helpful when
a class is wrapping one or more "containers" (Array, Hash, Set,...) and
you don't want to expose the entire interface of the container outside
of the class. You can do this reasonably well with : and := for a
single container but you can't have methods such as :container2 and
:container2= because of the parsing problems:

     container2[index] = val # is this self.container2.(index,val) or
                                # is this self.container2(index,val)

But if you allowed for arguments to :container= then you could do something
like:

     container2(index) # lookup contents of @container2[index]
     container2(index) = value # modify contents of @container2[index]

Right now if you want to package multiple containers in a class you need
to create set/get methods for each container that map to :/= for the
container or insert some sort of proxy class to avoid exposing the entire
container.

Is there a parsing hurdle to overcome or is there some conceptual reason
for prohibiting additional arguments to assignment methods?

···

On Oct 28, 2005, at 5:24 AM, Sean O'Halpin wrote:

o.(1,2,3) { |x| p x } # this is what you have to do in 1.8
o[1,2,3] { |x| p x } # this causes syntax error in 1.8 but works in 1.9

Hash:: is the only reason [1 => 2] is allowed, see: ruby-talk:163033

···

On Oct 28, 2005, at 7:03 AM, gwtmp01@mac.com wrote:

On Oct 28, 2005, at 5:24 AM, Sean O'Halpin wrote:

On 10/27/05, gwtmp01@mac.com <gwtmp01@mac.com> wrote:

Why are arguments to the '' method parsed differently
than a standard method?

Because () and are parsed differently. is primarily an array
reference (aref).

But *why* are they parsed differently? Just "because"?

--
Eric Hodel - drbrain@segment7.net - http://segment7.net
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04

I've looked through the parse.y file, so I understand the mechanical
reason why it doesn't work (it doesn't match the grammer) but I'm
not sure I've heard a design or philosophical reason why the grammar
is restrictive in this case. I haven't looked at it enough to
decide if extending the 'aref_args' construct would introduce
some ambiguity. It may be that it conflicts with the parsing of
literal arrays.

Gary Wright

···

On Oct 28, 2005, at 2:27 PM, Eric Hodel wrote:

Hash:: is the only reason [1 => 2] is allowed, see: ruby-talk:163033

IIRC, # and #call doing the same thing is a (relatively) recent addition to the language. Hash:: has been in Ruby since the first rev of Hash.

So it is possible it is simply an oversight.

···

On Oct 28, 2005, at 3:25 PM, gwtmp01@mac.com wrote:

On Oct 28, 2005, at 2:27 PM, Eric Hodel wrote:

Hash:: is the only reason [1 => 2] is allowed, see: ruby-talk:163033

I've looked through the parse.y file, so I understand the mechanical
reason why it doesn't work (it doesn't match the grammer) but I'm
not sure I've heard a design or philosophical reason why the grammar
is restrictive in this case. I haven't looked at it enough to
decide if extending the 'aref_args' construct would introduce
some ambiguity. It may be that it conflicts with the parsing of
literal arrays.

--
Eric Hodel - drbrain@segment7.net - http://segment7.net
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04

This was my first thought about this also, which is why
I posed the question--I wondered if I was missing something.

Gary Wright

···

On Oct 28, 2005, at 7:26 PM, Eric Hodel wrote:

IIRC, # and #call doing the same thing is a (relatively) recent addition to the language. Hash:: has been in Ruby since the first rev of Hash.

So it is possible it is simply an oversight.

Hey people

Just had quite an experience deploying my Rails app to Textdrive -
the Nitro gem was installed and there was a class in the Glue module
with the same name as my model... it took me two days to figure it out!

What I don't understand is how Glue got included in my project, since
all the Rails code I looked at makes no mention of it and I didn't
'require' it. Running irb, the offending class was not there,
running script/console, it was included!

How do I track down where it was included? Is there a way to list the
files that have been 'require'd? Is there a way to list all classes available -
and is there a way to tell if a class is 'custom' versus included with
the base Ruby installation?

Les

Leslie Viljoen wrote:

Hey people

Just had quite an experience deploying my Rails app to Textdrive -
the Nitro gem was installed and there was a class in the Glue module
with the same name as my model... it took me two days to figure it out!

What I don't understand is how Glue got included in my project, since
all the Rails code I looked at makes no mention of it and I didn't
'require' it. Running irb, the offending class was not there,
running script/console, it was included!

How do I track down where it was included? Is there a way to list the
files that have been 'require'd? Is there a way to list all classes available -
and is there a way to tell if a class is 'custom' versus included with
the base Ruby installation?

warn $".join( "\n" )

Maybe replace 'warn' with a call to some logger.

James

···

--

http://www.ruby-doc.org - The Ruby Documentation Site
http://www.rubyxml.com - News, Articles, and Listings for Ruby & XML
http://www.rubystuff.com - The Ruby Store for Ruby Stuff
http://www.jamesbritt.com - Playing with Better Toys

Save this in a file "loaded.rb":

···

On Friday 04 November 2005 09:56, Leslie Viljoen wrote:

Hey people

Just had quite an experience deploying my Rails app to Textdrive -
the Nitro gem was installed and there was a class in the Glue
module with the same name as my model... it took me two days to
figure it out!

What I don't understand is how Glue got included in my project,
since all the Rails code I looked at makes no mention of it and I
didn't 'require' it. Running irb, the offending class was not
there, running script/console, it was included!

How do I track down where it was included? Is there a way to list
the files that have been 'require'd? Is there a way to list all
classes available -
and is there a way to tell if a class is 'custom' versus included
with the base Ruby installation?

Les

--------------------------------
require 'rbconfig'

def find_ruby_lib(name)
    $LOAD_PATH.each { |dir|
        fn = File.join(dir, name)
        return dir, fn if File.exist? fn
    }
    raise "no such library loaded -- #{name}"
end

def is_std_library_dir?(dir)
    dir == Config::CONFIG["rubylibdir"] ||
        dir == Config::CONFIG["archdir"]
end

def print_loaded_libs
    $LOADED_FEATURES.each { |lib_name|
        dir, fn = find_ruby_lib(lib_name)
        puts lib_name
        puts " path: #{fn}"
        puts " Standard Library" if is_std_library_dir?(dir)
    }
end
--------------------------------
Then, before exiting (or at least after all require's)
do:
    require 'loaded'
    print_loaded_libs

Example:
    ruby -r optparse -r loaded -e 'print_loaded_libs'
    optparse.rb
      path: /usr/local/lib/ruby/1.8/optparse.rb
      Standard Library
    loaded.rb
      path: ./loaded.rb
    rbconfig.rb
      path: /usr/local/lib/ruby/1.8/i686-linux/rbconfig.rb
      Standard Library

HTH,
  Stefan

Stefan Lang wrote:

···

On Friday 04 November 2005 09:56, Leslie Viljoen wrote:

Hey people

Just had quite an experience deploying my Rails app to Textdrive -
the Nitro gem was installed and there was a class in the Glue
module with the same name as my model... it took me two days to
figure it out!

What I don't understand is how Glue got included in my project,
since all the Rails code I looked at makes no mention of it and I
didn't 'require' it. Running irb, the offending class was not
there, running script/console, it was included!

How do I track down where it was included? Is there a way to list
the files that have been 'require'd? Is there a way to list all
classes available -
and is there a way to tell if a class is 'custom' versus included
with the base Ruby installation?

Les
   
Save this in a file "loaded.rb":
--------------------------------
require 'rbconfig'

def find_ruby_lib(name)
   $LOAD_PATH.each { |dir|
       fn = File.join(dir, name)
       return dir, fn if File.exist? fn
   }
   raise "no such library loaded -- #{name}"
end

def is_std_library_dir?(dir)
   dir == Config::CONFIG["rubylibdir"] ||
       dir == Config::CONFIG["archdir"]
end

def print_loaded_libs
   $LOADED_FEATURES.each { |lib_name|
       dir, fn = find_ruby_lib(lib_name)
       puts lib_name
       puts " path: #{fn}"
       puts " Standard Library" if is_std_library_dir?(dir)
   }
end
--------------------------------
Then, before exiting (or at least after all require's)
do:
   require 'loaded'
   print_loaded_libs

Example:
   ruby -r optparse -r loaded -e 'print_loaded_libs'
   optparse.rb
     path: /usr/local/lib/ruby/1.8/optparse.rb
     Standard Library
   loaded.rb
     path: ./loaded.rb
   rbconfig.rb
     path: /usr/local/lib/ruby/1.8/i686-linux/rbconfig.rb
     Standard Library

HTH,
Stefan

Thanks - great solution!