Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Namespaces, inverted

7 views
Skip to first unread message

eero.sa...@kolumbus.fi

unread,
Dec 26, 2004, 3:14:48 PM12/26/04
to
Apologies if this topic has already been beaten to death.
Oh, and for the long-windedness.

Using modules for namespaces is somewhat unwieldy. I'd call it one
of the few evil parts of Ruby, although its implementation is
understandable when viewed from an OO standpoint. It's just
tiresome to A) invent and B) use unique namespaces in Ruby (and
by 'unique' I mean FooSoft Inc. wrapping all their libraries inside a
module FooSoft).

Of the current commercial languages, the package system of Java is
likely to be the best for flexibility and uniqueness, although it
does suffer from verbosity--and I wouldn't mind seeing Haskell-style
limited export capabilities, either. This, of course, should be
possible to do in Ruby 'on top' of the current system.

But why use unique namespaces? Modules are certainly useful for
partitioning a project into its constituent parts, but unique
namespaces are only useful in the context of that code being used
by another party where it can prevent namespace clashes. This, of
course, is highly site-dependent: it may not be necessary at all,
they may already have a package with the same 'unique' namespace,
they may not like the module name, etc. etc.

Why not have the 'client' decide if and which namespace to assign
to a foreign library? As in, if I want to use FooBar Inc.'s Math
package, they need not wrap it in module FooBar, but can just ship
it as Math. Then I just wrap it to Extern, my namespace for all
third-party modules. In current Ruby, I suppose this can be achieved
with something like (please correct if I'm wrong, this is off the top
of my head):
-----

# Toplevel
# This is just an aesthetically pleasing (?) implementation,
# I'm not sure about using String for the ascribed purpose.

def import file
file[:module].module_eval(IO.readlines(file[:name]).join)
end

class String
def as module
{:name => self, :module => module}
end

# Convenience
alias :in :as
alias :into :as
end

# Usage
import "foomath.rb".as Extern

rand = Extern::Math.random()

-----
Or is that just counterintuitive? It would at least conceptually
free the module construct from doing double duty as the namespace
construct.

The Ruby implementation is obviously somewhat slow but it could be
written to use the same routines as require()/load() with the
exception of doing the loading in the context of a given module. I
think the above 'implementation' also handles altogether non-moduled
files but I may be wrong in my it's-Sunday-morning-and-I'm-at-work
stupor.

E

Joel VanderWerf

unread,
Dec 27, 2004, 11:50:04 PM12/27/04
to

I like the idea very much, but there are some difficulties...


def import file
file[:module].module_eval(IO.readlines(file[:name]).join)
end

class String
def as mod

{:name => self, :module => mod}

end
end

module Extern; end
# It's a little awkward, but you gotta do this first, if
# you want to refer to the module as below.

libfile = "/usr/local/lib/ruby/1.8/complex.rb"
# One problem is the cumbersome path name to the library file.

import libfile.as(Extern)
# This fails because the library complex.rb tries to open an existing
# class, Numeric, and modify its contents. But instead it opens a new
# module, also called Numeric, that lives inside the wrapper. You can
# fix this in complex.rb (assuming you want to touch 3rd party code)
# by referring to the module as ::Numeric when you define it. But
# there's still a problem...

z = Extern::Complex(1,2)
# "undefined method `Complex' for Extern::Complex"
# This is due to the special effect of "def foo" at the top level in
# ruby--it defines an instance method of Object. But the effect within
# the wrapper module is very different. Again it's possible to modify
# the 3rd party code, as follows:
#
# class ::Object
# def Complex(a, b = 0)
# # ...
# end
# end


E S

unread,
Dec 28, 2004, 3:33:34 PM12/28/04
to
> Lähettäjä: Joel VanderWerf <vj...@PATH.Berkeley.EDU>
> Päiväys: 2004/12/27 ma PM 05:50:04 GMT+02:00
> Vastaanottaja: ruby...@ruby-lang.org (ruby-talk ML)
> Aihe: Re: Namespaces, inverted
>
> eero.sa...@kolumbus.fi wrote:
>> [Snip]


> I like the idea very much, but there are some difficulties...
>
> def import file
> file[:module].module_eval(IO.readlines(file[:name]).join)
> end
>
> class String
> def as mod
> {:name => self, :module => mod}
> end
> end
>
> module Extern; end
> # It's a little awkward, but you gotta do this first, if
> # you want to refer to the module as below.

I see you caught my use of 'module' there.
This is the easier problem to solve:

def import file
# Top-level
module_eval "module #{file[:module]}; end"


file[:module].module_eval(IO.readlines(file[:name]).join)
end

> libfile = "/usr/local/lib/ruby/1.8/complex.rb"
> # One problem is the cumbersome path name to the library file.

Could set it to look up simple filenames through
the standard ruby paths. Ideally, of course, one
wouldn't be importing the core libraries anyway,
since they're assumed to be habiting the global
namespace.

> import libfile.as(Extern)
> # This fails because the library complex.rb tries to open an existing
> # class, Numeric, and modify its contents. But instead it opens a new
> # module, also called Numeric, that lives inside the wrapper. You can
> # fix this in complex.rb (assuming you want to touch 3rd party code)
> # by referring to the module as ::Numeric when you define it. But
> # there's still a problem...

Here I am modifying String and I didn't think of anyone
doing the same :)

A solution would be to modify the loaded file String
before eval()ing it: do a

.. =~ /(?:module|class\s(\w+?)/
if $ALL_STANDARD_MODULE_AND_CLASS_NAMES.has_key? $1
# Escape the module name with ::
end

This (obviously) only works for standard modules and
classes and it doesn't take into account top-level
functions... (have to do def Kernel.foo explicitly if
necessary -any use of that function inside the imported
module would be sort of 'global' since it's in the same
scope anyway) but I'm not sure if it'd be necessary to
take precautions against anything else?

E

Robert Klemme

unread,
Dec 28, 2004, 3:37:10 PM12/28/04
to

"Joel VanderWerf" <vj...@PATH.Berkeley.EDU> schrieb im Newsbeitrag
news:41D0E5EF...@path.berkeley.edu...

I don't like the idea of the as method as it cannot be used reasonable in
other circumstances as module loading. The code really does not belong into
class String IMHO.

We have Kernel#load [1] with a similar functionality already. Currently it
accepts an additional parameter to enforce wrapping of the file in an
anonymous module. That could be extended to accept a module as well; that
then would be the module used. Then we can do

load "foo.rb" # not wrapped
load "foo.rb" # wrapped in an anonymous module
load "foo.rb", MyExternal # wrapped in MyExternal

Additionally / Alternatively it could be useful to have a per module require
like this:

class Module
def require(*files)
# puts "loading in module #{self}: #{files.inspect}"
@loaded ||= {}
files.each do |f|
unless @loaded[f]
# real load code that wraps contents of f in self
@loaded[f] = true
end
end
end
end

Then we could do

module Foo
require "bar"
end

and even if we do the same later again, "bar" is only loaded once (see [2]).

Sounds like an RCR candidate, doesn't it?

Kind regards

robert


[1] http://www.ruby-doc.org/core/classes/Kernel.html#M001744

[2] http://www.ruby-doc.org/core/classes/Kernel.html#M001745

Joel VanderWerf

unread,
Dec 28, 2004, 5:04:48 PM12/28/04
to
Robert Klemme wrote:
..

> We have Kernel#load [1] with a similar functionality already. Currently
> it accepts an additional parameter to enforce wrapping of the file in an
> anonymous module. That could be extended to accept a module as well;
> that then would be the module used. Then we can do
>
> load "foo.rb" # not wrapped
> load "foo.rb" # wrapped in an anonymous module
> load "foo.rb", MyExternal # wrapped in MyExternal

Wrapping is a nice idea (see raa:script for one approach), but the
wrapped lib will break if it depends on modifying classes or modules in
the global scope or defining global methods (the Complex example in my
previous email).

There is the idea the OP mentioned of parsing input files and rewriting
them to be explicit about the scope of definitions, automating the
kludgy hack I proposed for complex.rb. But that will break if the lib
uses dynamic code generation.

Maybe library writers should follow a standard of making their code
explicit about namespaces:

1. If you open and existing class, give an absolute path to it:

class ::Integer

rather than

class Integer

2. Global methods defs should be wrapped like this:

class ::Object
def global_meth ... end
end

3. References to constants (not just classes and modules) defined in the
lib should be relative:

class Foo
end

f = Foo.new

and not absolute:

f = ::Foo.new

(although I can't imagine that many libs have this problem).

4. Anything else to protect the lib from breakage when wrapping?


E S

unread,
Dec 28, 2004, 5:19:00 PM12/28/04
to
> Lähettäjä: "Robert Klemme" <bob....@gmx.net>

> "Joel VanderWerf" <vj...@PATH.Berkeley.EDU> schrieb im Newsbeitrag
> news:41D0E5EF...@path.berkeley.edu...
> > eero.sa...@kolumbus.fi wrote:
> > [Snip]

> > class String
> > def as mod
> > {:name => self, :module => mod}
> > end
> > end

> I don't like the idea of the as method as it cannot be used reasonable in
> other circumstances as module loading. The code really does not belong into
> class String IMHO.

Agreed. It just looked better :) I suppose a top-level method
would have done as nicely for this how-it-can-be-done-now
implementation.

> We have Kernel#load [1] with a similar functionality already. Currently it
> accepts an additional parameter to enforce wrapping of the file in an
> anonymous module. That could be extended to accept a module as well; that
> then would be the module used. Then we can do
>
> load "foo.rb" # not wrapped
> load "foo.rb" # wrapped in an anonymous module
> load "foo.rb", MyExternal # wrapped in MyExternal

Good. I had to rely on a 1.6 reference knowledge and didn't
recall the second parameter. Implementing it this way would,
certainly, require a change at the core level so an RCR might
be appropriate. I can write it up, I suppose.

I'm still partial to 'import' vs. 'require', though :)

> Additionally / Alternatively it could be useful to have a per module require
> like this:
>
> class Module
> def require(*files)
> # puts "loading in module #{self}: #{files.inspect}"
> @loaded ||= {}
> files.each do |f|
> unless @loaded[f]
> # real load code that wraps contents of f in self
> @loaded[f] = true
> end
> end
> end
> end
>
> Then we could do
>
> module Foo
> require "bar"
> end
>
> and even if we do the same later again, "bar" is only loaded once (see [2]).

Looks nice, although the functionality is somewhat
overlapping, no?

> Kind regards
>
> robert

E

Robert Klemme

unread,
Dec 29, 2004, 3:54:31 AM12/29/04
to

"E S" <eero.sa...@kolumbus.fi> schrieb im Newsbeitrag
news:20041228221857.GQPI599...@mta.imail.kolumbus.fi...

Great!

> I'm still partial to 'import' vs. 'require', though :)

Well, I'm open there. It's just that load and require have established
semantics: "load" reads the file on every occurrence while "require" loads
it only once (plus it does some file name magice to determine whether it's
"foo.rb", or "foo.so" / "foo.dll").

> > Additionally / Alternatively it could be useful to have a per module
require
> > like this:
> >
> > class Module
> > def require(*files)
> > # puts "loading in module #{self}: #{files.inspect}"
> > @loaded ||= {}
> > files.each do |f|
> > unless @loaded[f]
> > # real load code that wraps contents of f in self
> > @loaded[f] = true
> > end
> > end
> > end
> > end
> >
> > Then we could do
> >
> > module Foo
> > require "bar"
> > end
> >
> > and even if we do the same later again, "bar" is only loaded once (see
[2]).
>
> Looks nice, although the functionality is somewhat
> overlapping, no?

Yes, that's why I wrote "Additionally / Alternatively". :-) But keep in
mind the difference between load once and load always.

Regards

robert

Robert Klemme

unread,
Dec 30, 2004, 2:28:56 PM12/30/04
to

"Joel VanderWerf" <vj...@PATH.Berkeley.EDU> schrieb im Newsbeitrag
news:41D1D875...@path.berkeley.edu...

> Robert Klemme wrote:
> ..
>> We have Kernel#load [1] with a similar functionality already. Currently
>> it accepts an additional parameter to enforce wrapping of the file in an
>> anonymous module. That could be extended to accept a module as well;
>> that then would be the module used. Then we can do
>>
>> load "foo.rb" # not wrapped
>> load "foo.rb" # wrapped in an anonymous module
>> load "foo.rb", MyExternal # wrapped in MyExternal
>
> Wrapping is a nice idea (see raa:script for one approach), but the wrapped
> lib will break if it depends on modifying classes or modules in the global
> scope or defining global methods (the Complex example in my previous
> email).
>
> There is the idea the OP mentioned of parsing input files and rewriting
> them to be explicit about the scope of definitions, automating the kludgy
> hack I proposed for complex.rb. But that will break if the lib uses
> dynamic code generation.
>
> Maybe library writers should follow a standard of making their code
> explicit about namespaces:
>
> 1. If you open and existing class, give an absolute path to it:
>
> class ::Integer
>
> rather than
>
> class Integer

Totally agree. I guess this is seldom used these days.

> 2. Global methods defs should be wrapped like this:
>
> class ::Object
> def global_meth ... end
> end

That's really the same as case 1, isn't it?

> 3. References to constants (not just classes and modules) defined in the
> lib should be relative:
>
> class Foo
> end
>
> f = Foo.new
>
> and not absolute:
>
> f = ::Foo.new
>
> (although I can't imagine that many libs have this problem).

I think so, too.

> 4. Anything else to protect the lib from breakage when wrapping?

We would have to think about what happens if a wrapped module requires
another module. If that other module is a standard module you'll certainly
do not want it to be included in the wrapping namespace. If it belongs to
the same lib, then of course you want it wrapped. So the crucial question
is: how are these require's distinguished? I'm not 100% sure but I think a
simple 'require "foo"' will look relative as well as in global directories -
that might be a chance to distinguish...

As much as I like the original idea of wrapping classes in namespaces (much
the way that can be done with C++ because of #include) I believe this needs
more thought.

Kind regards

robert

E S

unread,
Dec 30, 2004, 5:55:12 PM12/30/04
to
> Lähettäjä: "Robert Klemme" <bob....@gmx.net>

> We would have to think about what happens if a wrapped module requires
> another module.

I don't think anything will. Try this:

module Test
require 'thread'
end

m = Test::Mutex.new

Should cause an error.

E

Robert Klemme

unread,
Dec 31, 2004, 7:41:51 AM12/31/04
to

"E S" <eero.sa...@kolumbus.fi> schrieb im Newsbeitrag
news:20041230225508.YVYV599...@mta.imail.kolumbus.fi...

There's probably a misunderstanding: I was not talking about actual behavior
but about the behavior of the suggested new include / require functionality
that was capable of wrapping loaded code in a module.

Regards

robert

0 new messages