[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Namespaces, inverted

e

12/26/2004 8:15:00 PM

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



4 Answers

Joel VanderWerf

12/28/2004 4:50:00 AM

0

eero.saynatkari@kolumbus.fi wrote:
> 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


Robert Klemme

12/28/2004 8:37:00 PM

0


"Joel VanderWerf" <vjoel@PATH.Berkeley.EDU> schrieb im Newsbeitrag
news:41D0E5EF.1040308@path.berkeley.edu...
> eero.saynatkari@kolumbus.fi wrote:
>> 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.ht...

[2] http://www.ruby-doc.org/core/classes/Kernel.ht...

Joel VanderWerf

12/28/2004 10:05:00 PM

0

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?


Robert Klemme

12/30/2004 7:29:00 PM

0


"Joel VanderWerf" <vjoel@PATH.Berkeley.EDU> schrieb im Newsbeitrag
news:41D1D875.8020501@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