Mixing Constants into objects

(gabriele renzi) #1

Hi gurus and nubys,

is there a way to add a constant into an object which is not a module/class?

Basically what I'd like is some kind of append_features/include that works on non-module objects.
Is my only chance to mess up with the singleton class by myself, or there is some existing method for this?

Thanks in advance

(David A. Black) #2

Hi --

Hi gurus and nubys,

is there a way to add a constant into an object which is not a module/class?

Basically what I'd like is some kind of append_features/include that works on non-module objects.

Is my only chance to mess up with the singleton class by myself, or there is some existing method for this?

If I understand you correctly, I think Kernel#extend is what you need:

   irb(main):001:0> s = ""
   => ""
   irb(main):002:0> module M; X=1; end
   => 1
   irb(main):003:0> s.extend(M)
   => ""
   irb(main):004:0> (class << s; self; end)::X
   => 1

I think that's the closest thing corresponding to "adding a constant"
to an arbitrary object (adding it to the object's singleton class).

David

···

On Sun, 14 Aug 2005, gabriele renzi wrote:

--
David A. Black
dblack@wobblini.net

(gabriele renzi) #3

David A. Black ha scritto:

Hi --

Hi gurus and nubys,

is there a way to add a constant into an object which is not a module/class?

Basically what I'd like is some kind of append_features/include that works on non-module objects.

Is my only chance to mess up with the singleton class by myself, or there is some existing method for this?

If I understand you correctly, I think Kernel#extend is what you need:

  irb(main):001:0> s = ""
  => ""
  irb(main):002:0> module M; X=1; end
  => 1
  irb(main):003:0> s.extend(M)
  => ""
  irb(main):004:0> (class << s; self; end)::X
  => 1

I think that's the closest thing corresponding to "adding a constant"
to an arbitrary object (adding it to the object's singleton class).

well, ri explicitly says for #extend:
" Adds to _obj_ the instance methods from each module given as a
  parameter"
and this is omitting Constants, which are available when something is #include'd
See example:

I won't be able to access singleton class constants from the singleton instance, it seems:

>> class Foo
>> def barer
>> Bar.new
>> end
>> end
=> nil
>> module M
>> class Bar
>> end
>> end
=> nil
>> foo=Foo.new
=> #<Foo:0x2d01428>
>> foo.extend M
=> #<Foo:0x2d01428>
>> foo.barer # I'd like to get a Bar instance here
NameError: uninitialized constant Foo2::Bar
         from (irb):12:in `barer'
         from (irb):21

I don't understand why, actually.

···

On Sun, 14 Aug 2005, gabriele renzi wrote:

(David A. Black) #4

Hi --

David A. Black ha scritto:

Hi --

Hi gurus and nubys,

is there a way to add a constant into an object which is not a module/class?

Basically what I'd like is some kind of append_features/include that works on non-module objects.

Is my only chance to mess up with the singleton class by myself, or there is some existing method for this?

If I understand you correctly, I think Kernel#extend is what you need:

  irb(main):001:0> s = ""
  => ""
  irb(main):002:0> module M; X=1; end
  => 1
  irb(main):003:0> s.extend(M)
  => ""
  irb(main):004:0> (class << s; self; end)::X
  => 1

I think that's the closest thing corresponding to "adding a constant"
to an arbitrary object (adding it to the object's singleton class).

well, ri explicitly says for #extend:
" Adds to _obj_ the instance methods from each module given as a
parameter"
and this is omitting Constants, which are available when something is #include'd

I'm not sure what you mean by "adding a constant to an object" if the
object isn't a Class or Module. For example:

   obj = Object.new
   obj.constants => no such method

All constants live in a class/module namespace. That's what I mean
when I say the closest you can get is adding it to an object's
singleton class.

See example:

I won't be able to access singleton class constants from the singleton instance, it seems:

class Foo
def barer
  Bar.new
end
end

=> nil

module M
class Bar
end
end

=> nil

foo=Foo.new

=> #<Foo:0x2d01428>

foo.extend M

=> #<Foo:0x2d01428>

foo.barer # I'd like to get a Bar instance here

NameError: uninitialized constant Foo2::Bar

Where did "Foo2" come from? I get Foo::Bar :slight_smile:

       from (irb):12:in `barer'
       from (irb):21

I don't understand why, actually.

I think it's this: when an instance method refers to a constant, that
constant is looked up in the class where the method is defined.
Therefore, in your example, Foo#barer looks for Foo::Bar.

So it's not the same as a method call, where it would be looked up in
the object's singleton class. I guess that's because constants really
belong to classes, not to objects generally. So when you ask for a
constant, the class you're in doesn't branch off and look in other
classes.

This may have to do with the way constant references are parsed, which
is kind of quasi-static.

Anyway, here's an illustration.

class Foo
  def x
    p X
  end
end

foo = Foo.new

class << foo
   X = 1
   Y = 1
   def y
     p Y
   end
end

foo.y => 1 (that's <foo's singleton class>::Y)
foo.x => uninitialized constant Foo::X
                     (foo's singleton class isn't involved)

David

···

On Sun, 14 Aug 2005, gabriele renzi wrote:

On Sun, 14 Aug 2005, gabriele renzi wrote:

--
David A. Black
dblack@wobblini.net

#5

class Foo
  def barer
   bar.new
  end
end
module M
  class Bar
  end
  def bar() Bar end
end
foo=Foo.new
foo.extend M
foo.barer #=> #<Foo::Bar:0x2940eb8>

(Devin Mullins) #6

As David said, the constant accessed in whatever class is specified by the class...end block you're currently in. Example:

class Moo; MOO = 'hi' end
class Foo; MOO = 'burp' end
class Foo
def Moo.greet; p MOO end
end
class Moo
def Moo.bark; p MOO end
end

irb(main):009:0> Moo.bark
"hi"
irb(main):010:0> Moo.greet
"burp"

Constants share this property with class variables.

So, if you want to inject a factory into your class, you're going to have to use a different method, or play around with const_set.

Devin

(gabriele renzi) #7

David A. Black ha scritto:

well, ri explicitly says for #extend:
" Adds to _obj_ the instance methods from each module given as a
parameter"
and this is omitting Constants, which are available when something is #include'd

I'm not sure what you mean by "adding a constant to an object" if the
object isn't a Class or Module. For example:

  obj = Object.new
  obj.constants => no such method

All constants live in a class/module namespace. That's what I mean
when I say the closest you can get is adding it to an object's
singleton class.

you're right as usual :slight_smile:
what I meant is adding them *somewhere* so that they're available to the object. Basically I wondered if ruby had something able to do the singleton class hackery withouth me noticing it, like extend does for instance methods (but after reading the rest now I understand why this can't work)

NameError: uninitialized constant Foo2::Bar

Where did "Foo2" come from? I get Foo::Bar :slight_smile:

emh.. evil little creatures acting on my desktop, I'd say..

       from (irb):12:in `barer'
       from (irb):21

I don't understand why, actually.

I think it's this: when an instance method refers to a constant, that
constant is looked up in the class where the method is defined.
Therefore, in your example, Foo#barer looks for Foo::Bar.

So it's not the same as a method call, where it would be looked up in
the object's singleton class. I guess that's because constants really
belong to classes, not to objects generally. So when you ask for a
constant, the class you're in doesn't branch off and look in other
classes.

ah, this makes a lot of sense, thanks

This may have to do with the way constant references are parsed, which
is kind of quasi-static.

mh.. maybe "lexical" is the better word ?

Anyway, here's an illustration.

class Foo
def x
   p X
end
end

foo = Foo.new

class << foo
  X = 1
  Y = 1
  def y
    p Y
  end
end

foo.y => 1 (that's <foo's singleton class>::Y)
foo.x => uninitialized constant Foo::X
                    (foo's singleton class isn't involved)

thanks for the explanation, this makes sense now.

(David A. Black) #8

Hi --

···

On Mon, 15 Aug 2005, gabriele renzi wrote:

I wrote:

This may have to do with the way constant references are parsed, which
is kind of quasi-static.

mh.. maybe "lexical" is the better word ?

I think it's more than that. What I should have said was: constant
references are resolved at parse-time, rather than run-time. At least
I think that's the case.

David

--
David A. Black
dblack@wobblini.net

(Caleb Clausen) #9

Just how constant lookups work is something of a mystery to me in some
cases... it's clearly less dynamic than method lookup, but it is
run-time lookup, not compile time:

irb(main):001:0> class K
irb(main):002:1> Foo=1
irb(main):003:1> end
=> 1
irb(main):004:0> def fk; K::Foo end
=> nil
irb(main):005:0> fk
=> 1
irb(main):006:0> class K
irb(main):007:1> Foo=2
irb(main):008:1> end
(irb):7: warning: already initialized constant Foo
=> 2
irb(main):009:0> fk
=> 2

···

On 8/14/05, David A. Black <dblack@wobblini.net> wrote:

On Mon, 15 Aug 2005, gabriele renzi wrote:
> mh.. maybe "lexical" is the better word ?

I think it's more than that. What I should have said was: constant
references are resolved at parse-time, rather than run-time. At least
I think that's the case.

(Wilson Bilkovich) #10

My (possibly totally off-base) understanding is that, as there is no
'compile' step in Ruby, the constants get 'burned' into the AST, as do
things like regular expression literals, between forward-slashes.
That would explain why they "look" like runtime evaluations, but they
aren't the same kind of citizen as variables.

···

On 8/15/05, Caleb Clausen <vikkous@gmail.com> wrote:

On 8/14/05, David A. Black <dblack@wobblini.net> wrote:
> On Mon, 15 Aug 2005, gabriele renzi wrote:
> > mh.. maybe "lexical" is the better word ?
>
> I think it's more than that. What I should have said was: constant
> references are resolved at parse-time, rather than run-time. At least
> I think that's the case.

Just how constant lookups work is something of a mystery to me in some
cases... it's clearly less dynamic than method lookup, but it is
run-time lookup, not compile time:

irb(main):001:0> class K
irb(main):002:1> Foo=1
irb(main):003:1> end
=> 1
irb(main):004:0> def fk; K::Foo end
=> nil
irb(main):005:0> fk
=> 1
irb(main):006:0> class K
irb(main):007:1> Foo=2
irb(main):008:1> end
(irb):7: warning: already initialized constant Foo
=> 2
irb(main):009:0> fk
=> 2

(Joel VanderWerf) #11

Caleb Clausen wrote:

mh.. maybe "lexical" is the better word ?

I think it's more than that. What I should have said was: constant
references are resolved at parse-time, rather than run-time. At least
I think that's the case.

Just how constant lookups work is something of a mystery to me in some
cases... it's clearly less dynamic than method lookup, but it is
run-time lookup, not compile time:

irb(main):001:0> class K
irb(main):002:1> Foo=1
irb(main):003:1> end
=> 1
irb(main):004:0> def fk; K::Foo end
=> nil
irb(main):005:0> fk
=> 1
irb(main):006:0> class K
irb(main):007:1> Foo=2
irb(main):008:1> end
(irb):7: warning: already initialized constant Foo
=> 2
irb(main):009:0> fk
=> 2

Constant binding is normally static:

class K
Foo=1
end

def fk; K::Foo end

fk

class K
Foo=2
end

p fk # ==> 2

module M
  class K
    Foo=3
  end
  p fk # ==> 2
  p K::Foo # ==> 3
end

# However, you can force dynamic constant binding:

def sfk; self::K::Foo; end

module M
  p sfk # ==> 3
end

···

On 8/14/05, David A. Black <dblack@wobblini.net> wrote:

On Mon, 15 Aug 2005, gabriele renzi wrote:

--
      vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407