An alternative to the class Foo < Struct.new(vars) idiom and SuperStruct

I wrote this a couple weeks ago
( http://eigenclass.org/hiki.rb?struct-alike+class+definition ) and was
considering whether it deserves an actual release. The implementation is
simple; taking just 40 lines of code, it allows you to do stuff like

聽聽class MyClass < SuperClass.new(:a, :b) # doubly appropriate :slight_smile:
聽聽聽聽def sum
聽聽聽聽聽聽@a + @b
聽聽聽聽end
聽聽end
聽聽a = MyClass.new(1, 1)
聽聽a # => #<MyClass:0xb7dfd254 @b=1, @a=1>
聽聽a.sum # => 2

The generated class uses normal instance variables unlike Struct.new, and
creates the accessors for you.

You also get keyword arguments for free:

聽聽b = MyClass.new :a => 1, :b => 41
聽聽b.sum # => 42
聽聽b # => #<MyClass:0xb7dfd024 @b=41, @a=1>
聽聽b.b # => 41

Default values are handled as follows:

聽聽class Foo < SuperClass.new(:text, :times) { @times ||= 2 }
聽聽聽聽def greeting
聽聽聽聽聽聽(["Hello, #{@text}"] * times).join("\n")
聽聽聽聽end
聽聽end

聽聽Foo.new("SuperClass", 2).greeting # => "Hello, SuperClass\nHello, SuperClass"
聽聽Foo.new(:text => "world").greeting # => "Hello, world\nHello, world"

Unlike Struct.new, you can use SuperClass to generate classes in the middle
of the inheritance chain:

聽聽class X
聽聽聽聽attr_reader :foo
聽聽聽聽def initialize(foo = 1)
聽聽聽聽聽聽@foo = foo
聽聽聽聽end
聽聽end

聽聽class Y < SuperClass.new(:bar, :baz, X) {@baz ||= 10; initialize_super(@baz + 1) }
聽聽聽聽def fubar; @foo + @baz end
聽聽end

聽聽Y.new(10, 1).foo # => 2
聽聽Y.new(:bar => 1).fubar # => 21

I have an extended implementation that also creates #eql?, #hash and #==
methods with selectable semantics.

Here's the basic implementation:

# Copyright (C) 2006 Mauricio Fernandez <mfp@acm.org> http://eigenclass.org
# Use and distribution under the same terms as Ruby.
class SuperClass
聽聽def self.new_class(accessor_type, *args, &block)
聽聽聽聽parent = args.pop if Class === args.last
聽聽聽聽parent ||= Object
聽聽聽聽unless args.size > 1
聽聽聽聽聽聽raise ArgumentError, "No point in using SuperClass for a single argument!"
聽聽聽聽end
聽聽聽聽Class.new(parent) do
聽聽聽聽聽聽@initialize_args = args.map{|x| "@#{x}".intern}
聽聽聽聽聽聽class << self; attr_reader :initialize_args end
聽聽聽聽聽聽case accessor_type
聽聽聽聽聽聽when :ro : attr_reader(*args)
聽聽聽聽聽聽when :rw : attr_accessor(*args)
聽聽聽聽聽聽end
聽聽聽聽聽聽
聽聽聽聽聽聽define_method(:initialize) do |*a|
聽聽聽聽聽聽聽聽args.each{|name| instance_variable_set("@#{name}", nil) }
聽聽聽聽聽聽聽聽if a.size == 1 && Hash === a[0]
聽聽聽聽聽聽聽聽聽聽args.each{|name| instance_variable_set("@#{name}", a[0][name.to_sym])}
聽聽聽聽聽聽聽聽elsif a.size != args.size
聽聽聽聽聽聽聽聽聽聽raise ArgumentError,
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽"wrong number of arguments (#{a.size} for #{args.size})"
聽聽聽聽聽聽聽聽else
聽聽聽聽聽聽聽聽聽聽args.each_with_index{|name, i| instance_variable_set("@#{name}", a[i])}
聽聽聽聽聽聽聽聽end
聽聽聽聽聽聽聽聽instance_eval(&block) if block
聽聽聽聽聽聽end

聽聽聽聽聽聽if block
聽聽聽聽聽聽聽聽super_meth = parent.instance_method(:initialize)
聽聽聽聽聽聽聽聽define_method(:initialize_super){|*a| super_meth.bind(self).call(*a) }
聽聽聽聽聽聽聽聽private :initialize_super
聽聽聽聽聽聽end
聽聽聽聽end
聽聽end

聽聽def self.new(*args, &block); new_class(:ro, *args, &block) end
聽聽def self.new_rw(*args, &block); new_class(:rw, *args, &block) end
end

路路路

--
Mauricio Fernandez - http://eigenclass.org - singular Ruby

Mauricio Fernandez wrote:

I wrote this a couple weeks ago
( http://eigenclass.org/hiki.rb?struct-alike+class+definition ) and was
considering whether it deserves an actual release. The implementation is
simple; taking just 40 lines of code, it allows you to do stuff like

  class MyClass < SuperClass.new(:a, :b) # doubly appropriate :slight_smile:
    def sum
      @a + @b
    end
  end
  a = MyClass.new(1, 1)
  a # => #<MyClass:0xb7dfd254 @b=1, @a=1>
  a.sum # => 2

The generated class uses normal instance variables unlike Struct.new, and
creates the accessors for you.

You also get keyword arguments for free:

  b = MyClass.new :a => 1, :b => 41
  b.sum # => 42
  b # => #<MyClass:0xb7dfd024 @b=41, @a=1>
  b.b # => 41

Default values are handled as follows:

  class Foo < SuperClass.new(:text, :times) { @times ||= 2 }
    def greeting
      (["Hello, #{@text}"] * times).join("\n")
    end
  end

  Foo.new("SuperClass", 2).greeting # => "Hello, SuperClass\nHello, SuperClass"
  Foo.new(:text => "world").greeting # => "Hello, world\nHello, world"

Unlike Struct.new, you can use SuperClass to generate classes in the middle
of the inheritance chain:

  class X
    attr_reader :foo
    def initialize(foo = 1)
      @foo = foo
    end
  end

  class Y < SuperClass.new(:bar, :baz, X) {@baz ||= 10; initialize_super(@baz + 1) }
    def fubar; @foo + @baz end
  end

  Y.new(10, 1).foo # => 2
  Y.new(:bar => 1).fubar # => 21

I have an extended implementation that also creates #eql?, #hash and #==
methods with selectable semantics.

Here's the basic implementation:

# Copyright (C) 2006 Mauricio Fernandez <mfp@acm.org> http://eigenclass.org
# Use and distribution under the same terms as Ruby.
class SuperClass
  def self.new_class(accessor_type, *args, &block)
    parent = args.pop if Class === args.last
    parent ||= Object
    unless args.size > 1
      raise ArgumentError, "No point in using SuperClass for a single argument!" end
    Class.new(parent) do
      @initialize_args = args.map{|x| "@#{x}".intern}
      class << self; attr_reader :initialize_args end
      case accessor_type
      when :ro : attr_reader(*args)
      when :rw : attr_accessor(*args)
      end
            define_method(:initialize) do |*a|
        args.each{|name| instance_variable_set("@#{name}", nil) }
        if a.size == 1 && Hash === a[0]
          args.each{|name| instance_variable_set("@#{name}", a[0][name.to_sym])}
        elsif a.size != args.size
          raise ArgumentError, "wrong number of arguments (#{a.size} for #{args.size})"
        else
          args.each_with_index{|name, i| instance_variable_set("@#{name}", a[i])}
        end
        instance_eval(&block) if block
      end

      if block
        super_meth = parent.instance_method(:initialize)
        define_method(:initialize_super){|*a| super_meth.bind(self).call(*a) }
        private :initialize_super
      end
    end
  end

  def self.new(*args, &block); new_class(:ro, *args, &block) end
  def self.new_rw(*args, &block); new_class(:rw, *args, &block) end
end

I'm sold. This will be great for easily defining event types in EventMachine. Why the complicated default argument values though? For simple cases how about just using hash syntax:

MyClass = SuperClass.new(:a => 2, :b => 3)

hi mauricio-

thought i'd just chime in here and show how this would be done with traits
since it has overlapping aims:

class MyClass < SuperClass.new(:a, :b) # doubly appropriate :slight_smile:
   def sum
     @a + @b
   end
end
a = MyClass.new(1, 1)
a # => #<MyClass:0xb7dfd254 @b=1, @a=1>
a.sum # => 2

The generated class uses normal instance variables unlike Struct.new, and
creates the accessors for you.

You also get keyword arguments for free:

b = MyClass.new :a => 1, :b => 41
b.sum # => 42
b # => #<MyClass:0xb7dfd024 @b=41, @a=1>
b.b # => 41

     harp:~ > cat a.rb
     require 'traits'
     class MyClass
       include TraitInit
       trait_initialize 'a', 'b'
       def sum() a + b end
     end

     a = MyClass.new 40, 2
     p a.a
     p a.b
     p a.sum

     a = MyClass.new 'b' => 2, 'a' => 40
     p a.a
     p a.b
     p a.sum

     p MyClass.traits

     harp:~ > ruby a.rb
     40
     2
     42
     40
     2
     42
     [["a", "a="], ["b", "b="]]

Default values are handled as follows:

class Foo < SuperClass.new(:text, :times) { @times ||= 2 }
   def greeting
     (["Hello, #{@text}"] * times).join("\n")
   end
end

Foo.new("SuperClass", 2).greeting # => "Hello, SuperClass\nHello, SuperClass"
Foo.new(:text => "world").greeting # => "Hello, world\nHello, world"

     harp:~ > cat a.rb
     require 'traits'

     class MyClass
       include TraitInit
       trait_initialize 'a' => 40, 'b' => 2
       def sum() a + b end
     end

     class MyClass2 < MyClass; end

     p MyClass.new.sum

     p MyClass2.new.sum

     harp:~ > ruby a.rb
     42

note that it works with inheritence too.

Unlike Struct.new, you can use SuperClass to generate classes in the middle
of the inheritance chain:

class X
   attr_reader :foo
   def initialize(foo = 1)
     @foo = foo
   end
end

class Y < SuperClass.new(:bar, :baz, X) {@baz ||= 10; initialize_super(@baz + 1) }
   def fubar; @foo + @baz end
end

Y.new(10, 1).foo # => 2
Y.new(:bar => 1).fubar # => 21

     harp:~ > cat a.rb
     require 'traits'

     class X
       include TraitInit
       trait_initialize
       trait :foo => 1
     end

     class Y < X
       trait :bar, :baz => 10
       trait(:foo){ baz + 1 }
       def fubar() foo + baz end
     end

     p Y.new(:bar => 10, :baz => 1).foo
     p Y.new(:bar => 1).fubar

     harp:~ > ruby a.rb
     2
     21

traits is here if anyone is interested

   http://rubyforge.org/projects/codeforpeople/
   http://codeforpeople.com/lib/ruby/traits

kind regards.

-a

路路路

On Thu, 1 Jun 2006, Mauricio Fernandez wrote:
--
be kind whenever possible... it is always possible.
- h.h. the 14th dali lama

The problem with this is that it makes positional arguments impossible, since
the Hash doesn't preserve the order; maybe

    MyClass = SuperClass.new(:a, 2, :b, 3)
or
    MyClass = SuperClass.new(:a, :b, :a => 2, :b => 3)
    # (don't like the repetition though)
?

Anyway, I realized I can remove a couple lines from SuperClass to turn the
following (which I forgot to show in the previous msg) into the preferred
style:
  # b is optional only when using the keyword arg style
  class Foo < SuperClass.new(:a, :b)
    def initialize(*args)
      super
      @b ||= "foo"
    end
  end

which will look better than passing a block to SuperClass.new for most people.

Maybe I should allow both styles:
* mandatory keyword arguments, with default values specified in the
  SuperClass.new call (have to define how, two possibilities given above but
  there's probably a better way)
* either full positional arguments or keyword arguments (at least one required
  to discriminate), default values set manually in #initialize as in the above
  example.

The desired one could be specified either via an explicit call
(SuperClass.new_kw_args vs. SuperClass.new_positional ?) or implicitly by the
arguments given to SuperClass.new.

It's all easy to implement but there are many options...

路路路

On Thu, Jun 01, 2006 at 09:33:29PM +0900, Jeff Rose wrote:

I'm sold. This will be great for easily defining event types in
EventMachine. Why the complicated default argument values though? For
simple cases how about just using hash syntax:

MyClass = SuperClass.new(:a => 2, :b => 3)

--
Mauricio Fernandez - http://eigenclass.org - singular Ruby

thought i'd just chime in here and show how this would be done with traits
since it has overlapping aims:

[...]

Please indulge some quick questioning on my part; all the answers are in the
sources but my eyeballs hurt a bit atm. and I'd prefer to avoid reading
traits-0.9.1.rb's ~700 lines of code :slight_smile: After a cursory look, it's clear
traits and SuperClass are in different leagues. SuperClass goes for minimalism
and meta-programming self-restraint (no methods added to core classes, no
hooks used...); traits seems to do so much more and pays the cost (I can see a
load of methods/instance variables being added at Object's level,
Object.singleton_method_added, etc.).

    harp:~ > cat a.rb
    require 'traits'
    class MyClass
      include TraitInit
      trait_initialize 'a', 'b'
      def sum() a + b end
    end

Would def sum; @a + @b end also work?
Plain instance variables are the main appeal of SuperStruct or SuperClass.

>Unlike Struct.new, you can use SuperClass to generate classes in the middle
>of the inheritance chain:
>

[...]

    harp:~ > cat a.rb
    require 'traits'

    class X
      include TraitInit
      trait_initialize
      trait :foo => 1
    end

    class Y < X
      trait :bar, :baz => 10
      trait(:foo){ baz + 1 }
      def fubar() foo + baz end
    end

    p Y.new(:bar => 10, :baz => 1).foo
    p Y.new(:bar => 1).fubar

Interesting. Can this be made to work if X is defined as
  class X
    attr_reader :foo
    def initialize(foo); @foo = foo end
  end
and doesn't include TraitInit, or when it does something non-trivial in
#initialize? In other words, is it possible to have a single class in the
hierarchy use traits' trait_initialize without touching the rest?

And is the block evaluated every time one calls #foo, or only once?

One last question: does trait provide some mechanism to create #==, #hash and
#eql?, and if so, are they static-ish (only considering instance
variables/traits at the moment they were created) or dynamic (taking into
account traits defined later)? I hesitated about which one would be better,
and finally added both to SuperClass, but maybe there's something for/against
one of them.

Thanks,

路路路

On Fri, Jun 02, 2006 at 12:27:32AM +0900, ara.t.howard@noaa.gov wrote:

On Thu, 1 Jun 2006, Mauricio Fernandez wrote:

--
Mauricio Fernandez - http://eigenclass.org - singular Ruby

thought i'd just chime in here and show how this would be done with traits
since it has overlapping aims:

[...]

Please indulge some quick questioning on my part; all the answers are in the
sources but my eyeballs hurt a bit atm. and I'd prefer to avoid reading
traits-0.9.1.rb's ~700 lines of code :slight_smile: After a cursory look, it's clear
traits and SuperClass are in different leagues. SuperClass goes for minimalism
and meta-programming self-restraint (no methods added to core classes, no
hooks used...); traits seems to do so much more and pays the cost (I can see a
load of methods/instance variables being added at Object's level,
Object.singleton_method_added, etc.).

all true. traits does alot. in particular it gives 'pols' semantics to
default values inherited via include or normal inheritence - something which
is actually tricky to do in ruby.

    harp:~ > cat a.rb
    require 'traits'
    class MyClass
      include TraitInit
      trait_initialize 'a', 'b'
      def sum() a + b end
    end

Would def sum; @a + @b end also work?
Plain instance variables are the main appeal of SuperStruct or SuperClass.

yes.

Unlike Struct.new, you can use SuperClass to generate classes in the middle
of the inheritance chain:

[...]

    harp:~ > cat a.rb
    require 'traits'

    class X
      include TraitInit
      trait_initialize
      trait :foo => 1
    end

    class Y < X
      trait :bar, :baz => 10
      trait(:foo){ baz + 1 }
      def fubar() foo + baz end
    end

    p Y.new(:bar => 10, :baz => 1).foo
    p Y.new(:bar => 1).fubar

Interesting. Can this be made to work if X is defined as
class X
   attr_reader :foo
   def initialize(foo); @foo = foo end
end
and doesn't include TraitInit, or when it does something non-trivial in
#initialize? In other words, is it possible to have a single class in the
hierarchy use traits' trait_initialize without touching the rest?

yes. trait_initialize is just a hook to do this

   def initialize *argv
     trait_init *argv
   end

so any class may just use trait_init directly.

And is the block evaluated every time one calls #foo, or only once?

only once. you can do

   trait 't' => 'simple_default_value'

or

   trait('t'){ 'default_deferred_evaluated_in_context_of_self' }

One last question: does trait provide some mechanism to create #==, #hash
and #eql?, and if so, are they static-ish (only considering instance
variables/traits at the moment they were created) or dynamic (taking into
account traits defined later)?

one neat thing about traits is that they are remembered dynamically and in the
order defined. eg.

   class C
     trait 'a'
     trait 'b'
     class_trait 'c'
   end

   p C.traits #=> [['a', 'a='], ['b', 'b=']]
   p C.rtraits #=> ['a', 'b']

   p C.class_traits #=> [['c', 'c=']]
   p C.class_rtraits #=> ['c']

so it's trival do do

   module TraitsEqual
     def eql other
       to_hash.eql other.to_hash
     end
     def to_hash
       self.class.rtraits.inject({}){|h,t| h.update t => send(t)}
     end
   end

I hesitated about which one would be better, and finally added both to
SuperClass, but maybe there's something for/against one of them.

it's totally dynamic in traits - eg i go all the way up the chain and include
any traits added later. it is a tough choice. for my use case i wanted this:

   module Properites
     'width' => 42
     'height' => 42
   end

   class C
     include Properties
     trait('size'){ width * height }

     # this gets width, height, and size
     def to_hash
       self.class.rtraits.inject({}){|h,t| h.update t => send(t)}
     end
   end

but i could see one going either way

Thanks,

sure. it's all fun stuff eh?

cheers.

-a

路路路

On Fri, 2 Jun 2006, Mauricio Fernandez wrote:

On Fri, Jun 02, 2006 at 12:27:32AM +0900, ara.t.howard@noaa.gov wrote:

On Thu, 1 Jun 2006, Mauricio Fernandez wrote:

--
be kind whenever possible... it is always possible.
- h.h. the 14th dali lama

Mauricio Fernandez wrote:

路路路

On Thu, Jun 01, 2006 at 09:33:29PM +0900, Jeff Rose wrote:

I'm sold. This will be great for easily defining event types in EventMachine. Why the complicated default argument values though? For simple cases how about just using hash syntax:

MyClass = SuperClass.new(:a => 2, :b => 3)

The problem with this is that it makes positional arguments impossible, since
the Hash doesn't preserve the order; maybe

[snip]

That's another example of why I would like a data structure
that 1) had a convenient notation for literals and 2) was
"like" a Hash, but ordered.

Hal

Hal Fulton wrote:

That's another example of why I would like a data structure
that 1) had a convenient notation for literals and 2) was
"like" a Hash, but ordered.

Facet's has Association which gives one something quite like that.

  [ :a >> 1, :b >> 2 ]

Of course it does take over the #>> operator, so it can't be used
inside any class that defines it. But that's not a big deal. It just
means you can't _extend_ the following classes with code that uses an
Association.

  Bignum
  Fixnum
  Date
  IPAddr
  Process::Status
  
T.

This is why I kind of like how in Perl => is just a synonym for comma (it is in ruby too, at least for Hash literals, the problem being that it forces a hash literal). then

def f(*args)
   args
end

f(1 => 2) #=> [1,2]

if you wanted a hash you could just do

def g(*args)
   Hash[ *args ]
end

Of course this takes away your ability to write things like

f(1, 2, :option => value)

but you could still do

f(1, 2, {:option => value })

I dunno. Maybe just use caller and some evil read the file in again (or SCRIPT_LINES__ to find out what order they called it in).

路路路

On Jun 1, 2006, at 11:13 PM, Hal Fulton wrote:

Mauricio Fernandez wrote:

On Thu, Jun 01, 2006 at 09:33:29PM +0900, Jeff Rose wrote:

I'm sold. This will be great for easily defining event types in EventMachine. Why the complicated default argument values though? For simple cases how about just using hash syntax:

MyClass = SuperClass.new(:a => 2, :b => 3)

The problem with this is that it makes positional arguments impossible, since
the Hash doesn't preserve the order; maybe

[snip]

That's another example of why I would like a data structure
that 1) had a convenient notation for literals and 2) was
"like" a Hash, but ordered.

Hal

Hal Fulton wrote:

That's another example of why I would like a data structure
that 1) had a convenient notation for literals and 2) was
"like" a Hash, but ordered.

Wow, it would be great if we then could use #to_hash to determine whether an object is a hash, and then not require that the return value of that method be an instance of Hash. Then we could create an ordered hash from scratch, and use it all the places we currently use a hash.

Wait...

Daniel

>Would def sum; @a + @b end also work?
>Plain instance variables are the main appeal of SuperStruct or SuperClass.

yes.

>Interesting. Can this be made to work if X is defined as
> class X
> attr_reader :foo
> def initialize(foo); @foo = foo end
> end
>and doesn't include TraitInit, or when it does something non-trivial in
>#initialize? In other words, is it possible to have a single class in the
>hierarchy use traits' trait_initialize without touching the rest?

yes.

[...]

>And is the block evaluated every time one calls #foo, or only once?

only once.

[...]

Thanks for taking the time to answer my questions; it seems traits is very
well-behaved indeed :slight_smile:

路路路

On Fri, Jun 02, 2006 at 02:35:26AM +0900, ara.t.howard@noaa.gov wrote:

--
Mauricio Fernandez - http://eigenclass.org - singular Ruby

that just gave me an idea:

     harp:~ > cat a.rb
     require 'arrayfields'
     class Map < Array
       class Pair < Array; end
       def initialize *pairs
         fields =
         pairs.each{|k, v| self << v and fields << k}
         self.fields = fields
       end
       def self.(*a, &b) new(*a, &b) end
     end
     class Symbol
       def >(value) Map::Pair[self, value] end
     end
     def Map(*a, &b) Map.new(*a, &b) end
     def map(*a, &b) Map.new(*a, &b) end

     m = map :a > 4, :b > 2
     p m
     p m[:a]
     p m[:b]
     p m.to_hash
     p m.keys

     harp:~ > ruby -r rubygems a.rb
     [4, 2]
     4
     2
     {:b=>2, :a=>4}
     [:a, :b]

what do you think?

-a

路路路

On Fri, 2 Jun 2006 transfire@gmail.com wrote:

Hal Fulton wrote:

That's another example of why I would like a data structure
that 1) had a convenient notation for literals and 2) was
"like" a Hash, but ordered.

Facet's has Association which gives one something quite like that.

[ :a >> 1, :b >> 2 ]

Of course it does take over the #>> operator, so it can't be used
inside any class that defines it. But that's not a big deal. It just
means you can't _extend_ the following classes with code that uses an
Association.

Bignum
Fixnum
Date
IPAddr
Process::Status

T.

--
be kind whenever possible... it is always possible.
- h.h. the 14th dali lama

Daniel Schierbeck wrote:

Hal Fulton wrote:

That's another example of why I would like a data structure
that 1) had a convenient notation for literals and 2) was
"like" a Hash, but ordered.

Wow, it would be great if we then could use #to_hash to determine whether an object is a hash, and then not require that the return value of that method be an instance of Hash. Then we could create an ordered hash from scratch, and use it all the places we currently use a hash.

Wait...

What is your point? Please elaborate...

Hal

Logan Capaldo <logancapaldo@gmail.com> writes:

This is why I kind of like how in Perl => is just a synonym for comma
(it is in ruby too, at least for Hash literals, the problem being
that it forces a hash literal). then

I stopped using Perl when I started using Ruby, but I seem to recall
that => in Perl does more than comma. E.g. {a => 1, b => 2} is mostly
equivalent to {'a', 1, 'b', 2}.

Ruby doesn't do the magic quoting.

Steve

Cool. Pair is a lot like Association but you've added the Map class
here. Of course you've limited yourself to symbol keys by using >.
Using Associations does have some limitations as far as being a full
fledged omap, since it's just an Array of Associations, but it would be
easy enough to combine it with your map class. Put the two together and
have:

  m = map :a>>4, :b>>2

Nice thing about Associations too is that have some other uses as well.
The only thing that would be better is a literal form and perhaps a
slighlty better operator.

T.

路路路

ara.t.howard@noaa.gov wrote:

On Fri, 2 Jun 2006 transfire@gmail.com wrote:

>
> Hal Fulton wrote:
>> That's another example of why I would like a data structure
>> that 1) had a convenient notation for literals and 2) was
>> "like" a Hash, but ordered.
>
> Facet's has Association which gives one something quite like that.
>
> [ :a >> 1, :b >> 2 ]
>
> Of course it does take over the #>> operator, so it can't be used
> inside any class that defines it. But that's not a big deal. It just
> means you can't _extend_ the following classes with code that uses an
> Association.
>
> Bignum
> Fixnum
> Date
> IPAddr
> Process::Status
>
> T.

that just gave me an idea:

     harp:~ > cat a.rb
     require 'arrayfields'
     class Map < Array
       class Pair < Array; end
       def initialize *pairs
         fields =
         pairs.each{|k, v| self << v and fields << k}
         self.fields = fields
       end
       def self.(*a, &b) new(*a, &b) end
     end
     class Symbol
       def >(value) Map::Pair[self, value] end
     end
     def Map(*a, &b) Map.new(*a, &b) end
     def map(*a, &b) Map.new(*a, &b) end

     m = map :a > 4, :b > 2
     p m
     p m[:a]
     p m[:b]
     p m.to_hash
     p m.keys

     harp:~ > ruby -r rubygems a.rb
     [4, 2]
     4
     2
     {:b=>2, :a=>4}
     [:a, :b]

what do you think?

True, but in perl you have to "force" the evaluation of variables anyway. => auto-quoting only works for barewords (\w+), as soon as you do something like "Hello, world" => 1 it's exactly the same as "Hello, world", 1

Whether or not it turns into a hash or an array is of course dependant on the left-hand side.

%hash = ( "a" => 1 );
@array = ( "a" => 1 );

Which I think may as well be equivalent to ruby's

{ "a" => 1 }
[ "a" => 1 ]

But (un?)fortunately they are not.

[ "a" => 1 ] in ruby is [ { "a" => 1 } ]

In fact in ruby => isn't equiv to comma, it's more like comma is equiv to => when surrounded by { }.

Getting way off topic here.

路路路

On Jun 5, 2006, at 5:59 PM, Steven Lumos wrote:

Logan Capaldo <logancapaldo@gmail.com> writes:

This is why I kind of like how in Perl => is just a synonym for comma
(it is in ruby too, at least for Hash literals, the problem being
that it forces a hash literal). then

I stopped using Perl when I started using Ruby, but I seem to recall
that => in Perl does more than comma. E.g. {a => 1, b => 2} is mostly
equivalent to {'a', 1, 'b', 2}.

Ruby doesn't do the magic quoting.

Steve