Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:798 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 19557 invoked from network); 8 Apr 2003 17:58:33 -0000 Received: from unknown (HELO mail.communityconnect.com) (209.10.169.57) by pb1.pair.com with SMTP; 8 Apr 2003 17:58:33 -0000 Received: from amavis by mail.communityconnect.com with scanned-ok (Exim 3.20 #1) id 192xMe-0002fX-00 for internals@lists.php.net; Tue, 08 Apr 2003 13:58:32 -0400 Received: from [192.168.2.240] (helo=dhcp-101-50.hq.communityconnect.com) by mail.communityconnect.com with esmtp (Exim 3.20 #1) id 192xMb-0002dA-00 for internals@lists.php.net; Tue, 08 Apr 2003 13:58:29 -0400 Date: Tue, 8 Apr 2003 13:58:36 -0400 (EDT) X-X-Sender: dan@dhcp-101-50.hq.communityconnect.com To: internals@lists.php.net Message-ID: MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII Subject: namespace problems From: dan@mail.communityconnect.com (Daniel Cowgill) Hello, (I've just started playing with PHP5 namespaces , so please bear with me -- there is an excellent chance I'm missing something.) Because my company manages a very large PHP application (a million lines) which is maintained and developed by 13 programmers, support for namespaces/modules is very exciting to us. I have a few concerns with how they are being implemented, however: 1. import * silently hides local names: namespace foo { function f() { print "foo::f()"; } class x { function x() { print "foo::x()"; } } } function f() { print "::f()"; } class x { function x() { print "::x()"; } } import * from foo; f(); // prints foo::f() new x(); // prints foo::x() This creates a situation where adding a definition to a source file may silently alter the behavior of any number of other files. The correct behavior IMHO is to override names "on demand," only if there is not a conflicting definition in the local namespace. I.e. local names always take precedence over names imported using import *. At the very least, however, I don't think the override should be silent. 2. Interestingly (and I think this is just a bug), functions and classes behave differently wrt to import * and duplicate definitions: namespace foo { function f() { print "foo::f()"; } class x { function x() { print "foo::x()"; } } } import * from foo; function f() { print "::f()"; } // OK class x { function x() { print "::x()"; } } // runtime error 3. There is no way to hide names from import (i.e. make a name non-exportable). I believe the lack of such a feature is going to cause problems unless import * is altered to let local names take precendence (see #1). Even if import * is changed, however, private namespace definitions will be sorely missed. I can't think of any serious implementation difficulties, and we already have the obvious keyword ("private"). 4. As a side note, import * behaves oddly in that the semantic meaning of multiple imports depends on their relative ordering: namespace foo { function f() { print "foo::f()"; } } namespace bar { function f() { print "bar::f()"; } } // (1) these two lines make global f point to bar::f import * from foo; import * from bar; // (2) these two lines make global f point to foo::f import * from bar; import * from foo; // (3) these two lines make global f point to bar::f import function f from foo; import * from bar; // (4) runtime error! import * from bar; import function f from foo; I find these highly problematic. (1) and (2) violate what I think should be a cardinal rule of language design -- that the order of _declarations_ should not significantly alter program meaning -- or they at least should not do so silently. (3) Is especially surprising because the user has explicitly asked for foo::f. And I assume it is simply a bug that (3) and (4) behave differently. This is a corollary to issue #1, and I think the same mechanism (on-demand overrides) can correct both without prohibitive runtime cost. In fact, I think it could easily be faster. 5. I believe all of the above problems are surmountable, but not this one: import in an included source file alters the scope of including source file: // a.php namespace foo { function f() {} } // b.php include "./a.php"; import * from foo; // c.php include "./b.php"; f(); // calls foo::f In a large application, this import will create a maintanence nightmare (especially when combined with the other problems listed above). I can guarantee that my company will relunctantly be forced to ban import statements in all but the "uppermost" file if import works like this. Adding names to a.php or import statements to b.php will wreak havoc with files that include b.php or c.php -- and the authors of b.php and c.php can't possibly predict which files will include it. The obvious solution is to avoid using import in libraries, which unfortunately makes namespaces far less attractive... they devolve into a minor convenience -- not worth the effort. There is a _very_ good reason that namespace mechanisms in other languages do not work like this. Now is the time to revise the feature, before it gets used in the real world and backwards compatibility becomes an issue. (I have several more comparatively minor--some utterly trivia--lissues with namespaces. I'll list them even though they are not as interesting and I have no real hope of seeing them approved, just for fun.) 6. Why is the import statement so verbose? I.e. why import function f, class x from foo; instead of import f, x from foo; Is it just to avoid looking up 'f' and 'x' in multiple symbol tables? If so, there's no reason to force users to type 'function' and 'class'. They should be optional. 7. It would be great if import * could be enhanced so that specific names could be excluded. Example syntax: import * from foo hiding function f, class x; (Or 'hide' or 'except' instead of 'hiding'.) I can't think of any serious implementation issues with this. 8. This is way out there, but I'd like to be able to declare namespace global variables, even if they couldn't be exported (I wouldn't want them to be). Unfortunately, I can't think of an elegant way to access them from functions while keeping them out of global scope. So, there are certainly implementation issues here. 9. Again, this one is also way out there, but very useful IMHO. It would be "neat" if namespace names were tied to physical layout, so that an import statement would subsume include/require. "Nested" namespace names would correspond to nested directory hierarchies.