Forward references?


#1

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


#2

On 26.05.2007 13:12, Peter S. 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


#3

In message removed_email_address@domain.invalid, Robert K. 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


#4

On 5/26/07, Peter S. removed_email_address@domain.invalid 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


#5

In message
removed_email_address@domain.invalid, “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. :slight_smile:

-s


#6

On 5/26/07, Peter S. removed_email_address@domain.invalid 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


#7

On 2007-05-26, Peter S. removed_email_address@domain.invalid 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 H.


#8

On 5/26/07, Jeremy H. removed_email_address@domain.invalid wrote:

On 2007-05-26, Peter S. removed_email_address@domain.invalid 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.


#9

In message
removed_email_address@domain.invalid, “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