Forum: Ruby Forward references?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
B8cfd5ec0f88bf5b5f2eedda7d1a0746?d=identicon&s=25 unknown (Guest)
on 2007-05-26 13:13
(Received via mailing list)
I am trying to figure out what the cleanest way is to express a class
that has
subclasses.

Imagine, if you will:
  class Foo
    def initialize
      @bar = Foo::Bar.new(self)
    end
  end

  class Foo::Bar
    def initialize(foo)
      @foo = foo
    end
  end

Is that a good rubyish way to do this?  If I put Foo::Bar in
its own file, should foo.rb be responsible for requiring it?
What originally got me thinking was that my habit is to put requires,
includes, and such at the top of the file... But then the
declaration "class Foo::Bar" refers to an undefined constant.

Of course, I can write:
  class Foo
  end
  require 'foo/bar.rb'

Is there a clear preference here in Ruby Style?

-s
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2007-05-26 13:25
(Received via mailing list)
On 26.05.2007 13:12, Peter Seebach wrote:
> I am trying to figure out what the cleanest way is to express a class that has
> subclasses.

First of all, your example is about nested classes and not subclasses.

>     end
>   end
>
> Is that a good rubyish way to do this?  If I put Foo::Bar in
> its own file, should foo.rb be responsible for requiring it?
> What originally got me thinking was that my habit is to put requires,
> includes, and such at the top of the file... But then the
> declaration "class Foo::Bar" refers to an undefined constant.

That's not an issue as long as you evaluate that only after the constant
has been defined:

irb(main):001:0> def test() Foo.new end
=> nil
irb(main):002:0> class Foo; end
=> nil
irb(main):003:0> test
=> #<Foo:0x7ff95f40>

> Of course, I can write:
>   class Foo
>   end
>   require 'foo/bar.rb'
>
> Is there a clear preference here in Ruby Style?

I would not put these in different files as Foo depends on Foo::Bar and
Foo::Bar depends on Foo.

I'd do

class Foo
   def initialize
     @bar = Bar.new self
   end

   class Bar
     def initialize(foo)
       @foo = foo
     end
   end
end

irb(main):012:0> Foo.new
=> #<Foo:0x7ff7883c @bar=#<Foo::Bar:0x7ff78738 @foo=#<Foo:0x7ff7883c
...>>>

Another question is whether you really want nested classes or maybe
rather classes in the same namespace.  Basically I create nested classes
if the nested class only makes sense in the context of the outer class.
  The situation is however different than in Java where all this is much
more strict (and thus clear).

Kind regards

  robert
B8cfd5ec0f88bf5b5f2eedda7d1a0746?d=identicon&s=25 unknown (Guest)
on 2007-05-26 13:30
(Received via mailing list)
In message <5bqjfiF2u1b1vU1@mid.individual.net>, Robert Klemme writes:
>First of all, your example is about nested classes and not subclasses.

I knew that.  Maybe I shouldn't be on decaf.

>end
Ooh.

>Another question is whether you really want nested classes or maybe
>rather classes in the same namespace.  Basically I create nested classes
>if the nested class only makes sense in the context of the outer class.
>  The situation is however different than in Java where all this is much
>more strict (and thus clear).

Classes in the same namespace might be a better choice.  I
think I may just do that.  Thinking about it, my assumption that
the nested classes only made sense in the context of an
outer class may be wrong.  Since I can't see a reason to enforce that
assumption, I'll just remove it, and move them all into a namespace
together.

-s
481b8eedcc884289756246e12d1869c1?d=identicon&s=25 Francis Cianfrocca (blackhedd)
on 2007-05-26 13:32
(Received via mailing list)
On 5/26/07, Peter Seebach <seebs@seebs.net> wrote:
>         end
> includes, and such at the top of the file... But then the
> declaration "class Foo::Bar" refers to an undefined constant.


The fact that "Foo::Bar" is undefined in Foo#initialize doesn't matter
to
Ruby's parser. The whole notion of "forward references" is really more
at
home in languages that build symbol-tables at compile time. Ruby just
deals
with the names themselves.

Given that, I'd look at the application semantics for a clue to your
question. Foo makes reference to Foo::Bar. If they're that closely
related,
why would you then not simply include the definition of Bar inside the
definition of Foo?

Two possible reasons: you want to segregate them textually to make your
code
easier to read, or you want to support multiple definitions of Bar, to
be
determined at runtime. In either case, you can place the require
statements
where it makes the most sense for the user of Foo. You can defend
yourself
against Bar being required ahead of Foo by coding it like this:

class Foo
    class Bar
      ...
    end
end
B8cfd5ec0f88bf5b5f2eedda7d1a0746?d=identicon&s=25 unknown (Guest)
on 2007-05-26 13:40
(Received via mailing list)
In message
<3a94cf510705260432r5157b607wae9cd16ce61aa6a7@mail.gmail.com>, "Fran
cis Cianfrocca" writes:
>The fact that "Foo::Bar" is undefined in Foo#initialize doesn't matter to
>Ruby's parser. The whole notion of "forward references" is really more at
>home in languages that build symbol-tables at compile time. Ruby just deals
>with the names themselves.

  class A::B
  end
  class A
  end
  --> t.rb:1: uninitialized constant A (NameError)

So I can't just do them in ANY order.

>Two possible reasons: you want to segregate them textually to make your code
>easier to read, or you want to support multiple definitions of Bar, to be
>determined at runtime. In either case, you can place the require statements
>where it makes the most sense for the user of Foo. You can defend yourself
>against Bar being required ahead of Foo by coding it like this:

The rationale was essentially to segregate them textually.  I like
having moderately self-contained things in separate files, even if they
have some necessary cohesion.

Hmm.

I had the brief notion of writing
  module A
    require 'b.rb'
  end
and ending up with a class named A::B, but B ends up outside
of A anyway.  So much for my clever idea.  :)

-s
481b8eedcc884289756246e12d1869c1?d=identicon&s=25 Francis Cianfrocca (blackhedd)
on 2007-05-26 14:14
(Received via mailing list)
On 5/26/07, Peter Seebach <seebs@seebs.net> wrote:
>
>
>         class A::B
>         end
>         class A
>         end
>         --> t.rb:1: uninitialized constant A (NameError)
>
> So I can't just do them in ANY order.



In this construction you're defining B inside the naming context of A,
which
doesn't exist when the *parser* (which obeys Ruby scoping rules like any
other Ruby program) needs to use the value of A. That's why I suggested
something like this:

class A
  class B
    def bar
    end
  end
end

class A
  def initialize
    @b = B.new
  end
end


You see the difference? You don't have to define class A before you
define
class B, but you DO need to *declare* A before you can present it to
Ruby's
parser in a construction like A::xxx.

Of course your original question related to style rather than parser
behavior. You don't say whether you come from a Java background, where
the
relationship between classes and source files is much stricter than it
is in
Ruby. With closely related Ruby classes like yours, I often define them
in
separate text blocks, in the same file:

module MyApplication
    class A
        def initialize
            @b = B.new
        end
    end
end

#------------------------------

module MyApplication
    class A
        class B
        end
    end
end
B8cfd5ec0f88bf5b5f2eedda7d1a0746?d=identicon&s=25 unknown (Guest)
on 2007-05-26 14:18
(Received via mailing list)
In message
<3a94cf510705260514u31029d2fr95bb8f96c5520c23@mail.gmail.com>, "Francis
Cianfrocca"
writes:
>In this construction you're defining B inside the naming context of A, which
>doesn't exist when the *parser* (which obeys Ruby scoping rules like any
>other Ruby program) needs to use the value of A.

Right.  Thus my forward-reference solution:

  class A
  end

  class A::B
  ...
  end

  class A
  ...
  end

Which is mostly picked up from C's "struct foo;" forward reference
declarations.

>You see the difference? You don't have to define class A before you define
>class B, but you DO need to *declare* A before you can present it to Ruby's
>parser in a construction like A::xxx.

Right.

>Of course your original question related to style rather than parser
>behavior. You don't say whether you come from a Java background, where the
>relationship between classes and source files is much stricter than it is in
>Ruby.

Mostly C and perl, although I read and write a fair number of languages
now.

>module MyApplication
>    class A
>        def initialize
>            @b = B.new
>        end
>    end
>end

>#------------------------------

>module MyApplication
>    class A
>        class B
>        end
>    end
>end

Makes sense.  I'm now doing:

  module MyApplication
    class A
    ...
    end

    class B
    ...
    end
  end

because really, I don't have an unambiguous top-level class, so using a
module
for namespace is clearer.

-s
15f985dc1d6a150767736a0c65e9ef35?d=identicon&s=25 Jeremy Henty (Guest)
on 2007-05-26 15:06
(Received via mailing list)
On 2007-05-26, Peter Seebach <seebs@seebs.net> wrote:

>   class Foo
>     def initialize
>       @bar = Foo::Bar.new(self)
>     end
>   end
>
> [snip]
>
> ...  If I put Foo::Bar in its own file, should foo.rb be responsible
> for requiring it?

I would say yes.  My rule of thumb is that any file that explicitly
calls some API must explicitly require/use/#include/... that API.
IOW, batteries *should* be included; don't force your users to make
extra trips to the shops.

Regards,

Jeremy Henty
481b8eedcc884289756246e12d1869c1?d=identicon&s=25 Francis Cianfrocca (blackhedd)
on 2007-05-26 17:01
(Received via mailing list)
On 5/26/07, Jeremy Henty <onepoint@starurchin.org> wrote:
>
> On 2007-05-26, Peter Seebach <seebs@seebs.net> wrote:
>  I would say yes.  My rule of thumb is that any file that explicitly
> calls some API must explicitly require/use/#include/... that API.
> IOW, batteries *should* be included; don't force your users to make
> extra trips to the shops.



I think your rule of thumb is absolutely right. Always make your code as
as
easy as possible to use.

However...
I have started, coding my source files *without* including all the
things
they might require, just to be ready for an eventual Ruby deployment
model
that pulls together pieces of code running in different processes, using
DI
to make sure everything is included at runtime. I know that must sound
weird. It does put more pressure on the users of a given code module,
since
they have to know more about the includes required by things that they
include.
This topic is locked and can not be replied to.