From: Robert Klemme Date: 2004-12-29T05:41:48+09:00 Subject: Re: Namespaces, inverted "Joel VanderWerf" 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.html#M001744 [2] http://www.ruby-doc.org/core/classes/Kernel.html#M001745