More fun with modules

Okay, I thought I understood modules. Then I try to factor out some
common ‘utility’ methods into a separate file so that I don’t have to
mock up some silly inheritance hierarchy just to share functions like
AddHref(below). But apparently I don’t understand how names
propogate. I figured that inside TestHarness::Bah, if it couldn’t
resolve the name in Bah, it would look next in TestHarness, and then
in the global namespace. Did I err? If so, how can I get what I
want? Do I need to introduce an artificial inheritance here, or is
there a way I’m not seeing to share the code?

Thanks,

-=Eric

$ cat testharness.rb
module TestHarness
def AddHref(url, text)
"#{text}"
end
end

$ cat testview.rb
require ‘testharness’

module TestHarness
class Bah
def myprint
puts AddHref(‘http://www.google.com/’, ‘Google’)
end
end
end

TestHarness::Bah.new.myprint

$ ruby testview.rb
testview.rb:6: in ‘myprint’: undefined method ‘AddHref’ for #TestHarness::Bah:0x4024e7e8 (NameError)
from testview.rb:11

-=Eric

···


Come to think of it, there are already a million monkeys on a million
typewriters, and Usenet is NOTHING like Shakespeare.
– Blair Houghton

You didn’t “include” TestHarness as part of Bah. If you don’t do
that, then you need to fully qualify AddHref.

-austin
– Austin Ziegler, austin@halostatue.ca on 2002.11.11 at 14.59.13

···

On Tue, 12 Nov 2002 04:46:43 +0900, Eric Schwartz wrote:

Okay, I thought I understood modules. Then I try to factor out
some common ‘utility’ methods into a separate file so that I don’t
have to mock up some silly inheritance hierarchy just to share
functions like AddHref(below). But apparently I don’t understand
how names propogate. I figured that inside TestHarness::Bah, if it
couldn’t resolve the name in Bah, it would look next in
TestHarness, and then in the global namespace. Did I err? If so,
how can I get what I want? Do I need to introduce an artificial
inheritance here, or is there a way I’m not seeing to share the
code?

Eric Schwartz wrote:

$ cat testharness.rb
module TestHarness

Did you mean to write
def TestHarness.AddHref(url, text)
here and make a class method? Here you only make a method that would
become part of a class that inclue the TestHarness module, not a method
that is plainly available.

···

def AddHref(url, text)
#{text}
end
end


([ Kent Dahl ]/)_ ~ [ http://www.stud.ntnu.no/~kentda/ ]/~
))_student
/(( _d L b_/ NTNU - graduate engineering - 5. year )
( __õ|õ// ) )Industrial economics and technological management(
_
/ö____/ (_engineering.discipline=Computer::Technology)

Eric Schwartz wrote:

$ cat testharness.rb
module TestHarness
def AddHref(url, text)
#{text}
end
end

$ cat testview.rb
require ‘testharness’

module TestHarness
class Bah
def myprint
puts AddHref(‘http://www.google.com/’, ‘Google’)
end
end
end

TestHarness::Bah.new.myprint

Try this:

class Bah
include TestHarness
def myprint

end
end

Method lookup doesn’t go thru enclosing modules, just included modules
(and superclasses, of course).

However, constant lookup does look thru enclosing modules, in addition
to included modules and superclasses:

module M
X = 1
class C
def foo
p X
end
end
end

M::C.new.foo # ==> 1

Joel VanderWerf vjoel@PATH.Berkeley.EDU writes:

Method lookup doesn’t go thru enclosing modules, just included modules
(and superclasses, of course).

However, constant lookup does look thru enclosing modules, in
addition to included modules and superclasses:

Ah, that explains it! I was using this facility to define a few
module-level global variables that I didn’t want to be /real/
globals, and it seems to me that the PoLS would indicate that what
works for constants works for other things, too. Why does it work
this way?

-=Eric

···


Come to think of it, there are already a million monkeys on a million
typewriters, and Usenet is NOTHING like Shakespeare.
– Blair Houghton

Eric Schwartz wrote:

Joel VanderWerf vjoel@PATH.Berkeley.EDU writes:

Method lookup doesn’t go thru enclosing modules, just included modules
(and superclasses, of course).

However, constant lookup does look thru enclosing modules, in
addition to included modules and superclasses:

Ah, that explains it! I was using this facility to define a few
module-level global variables that I didn’t want to be /real/
globals, and it seems to me that the PoLS would indicate that what
works for constants works for other things, too. Why does it work
this way?

One answer is: because methods can access the internal state of an
object. You want to be able to nest a class within another class,
without sharing state. For example:

class Space
class Point
attr_accessor :x, :y
def initialize x, y
@x = x; @y = y
end
end

attr_accessor :top_left, :bottom_right

def initialize x0, y0, x1, y1
@top_left = Point.new x0, y0
@bottom_right = Point.new x1, y1
end
end

space = Space.new 100, 100, 500, 200

tl = space.top_left

p tl.x # ==> 100

if method lookup for Point fell through to Space, we’d

be able to do this, which is senseless:

#p tl.bottom_right

Joel VanderWerf vjoel@PATH.Berkeley.EDU writes:

if method lookup for Point fell through to Space, we’d

be able to do this, which is senseless:

#p tl.bottom_right

That’s not what I’m talking about, though I can see how you got
there. I’m saying that it makes no sense to define a class inside a
module, and then have to include the module’s namespace inside
itself. To me, this looks needlessly redundant:

module Foo
class Bar
include Foo
def bah
thingy()
end
end

(assuming thingy() is defined in module Foo elsewhere)

I just defined Bar inside Foo, why should I have to include it all
over again?

-=Eric

···


Come to think of it, there are already a million monkeys on a million
typewriters, and Usenet is NOTHING like Shakespeare.
– Blair Houghton

Hi –

Joel VanderWerf vjoel@PATH.Berkeley.EDU writes:

if method lookup for Point fell through to Space, we’d

be able to do this, which is senseless:

#p tl.bottom_right

That’s not what I’m talking about, though I can see how you got
there. I’m saying that it makes no sense to define a class inside a
module, and then have to include the module’s namespace inside
itself. To me, this looks needlessly redundant:

module Foo
class Bar
include Foo
def bah
thingy()
end
end

(assuming thingy() is defined in module Foo elsewhere)

I just defined Bar inside Foo, why should I have to include it all
over again?

It’s not really ‘again’; defining a class and including a module are
very different operations. You’ve defined a class (Foo::Bar), but you
haven’t yet indicated, prior to the ‘include’, that you want the
instance methods defined in a particular module (Foo) to be instance
methods of that class. Like any class, Foo::Bar has to set up its
instance methods and method search chain explicitly, by inheritance,
method definition, and/or module inclusion.

All of which may sound like an elaborate way of saying, “You have to”
:slight_smile: But the model is very consistent. If classes started knowing
what else was defined in their enclosing modules – not just knowing,
but adding such methods to their instances – things could get very
messy…

I agree that stylistically it does feel a little weird for a class to
include the module in which it itself is defined. The best thing
would probably be to do a little design tweaking:

module Foo
module FooMod
def thingy
puts “Hi, I’m here”
end
end

class Bar
  include FooMod
end

end

Foo::Bar.new.thingy # Hi, I’m here

Here, Foo::Bar is on the same level as the module it includes, which
probably has a better feel to it.

David

···

On Tue, 12 Nov 2002, Eric Schwartz wrote:


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

I like this. Module Foo is used as a namespace, module FooMod is used
as a mixin. A clear separation of concerns. Little design tweaking,
big effect.

Regards,
Pit

···

On 12 Nov 2002 at 7:47, dblack@candle.superlink.net wrote:

(…)
I agree that stylistically it does feel a little weird for a class to
include the module in which it itself is defined. The best thing
would probably be to do a little design tweaking:

module Foo
module FooMod
def thingy
puts “Hi, I’m here”
end
end

class Bar
  include FooMod
end

end

Foo::Bar.new.thingy # Hi, I’m here

Here, Foo::Bar is on the same level as the module it includes, which
probably has a better feel to it.