Namespaces, inverted

G

Guest

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
 
J

Joel VanderWerf

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.

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
 
R

Robert Klemme

Joel VanderWerf said:
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.

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

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
 
J

Joel VanderWerf

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?
 
R

Robert Klemme

Joel VanderWerf said:
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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,521
Members
44,995
Latest member
PinupduzSap

Latest Threads

Top