1. Namespace (take 2)
- Posted by Derek Parnell <ddparnell at bigpond.com> Jun 26, 2001
- 437 views
Hi All, There have been lots of great suggestions and discussion on the namespace issue. Its changed some of my thinking about the problems it is trying to solve and the potential solutions. I don't think I've got a definitive solution yet in my head but here is my current thinking. ** Namespaces are optional. The default is "global". Just so existing code will still parse okay. e.g.. include graphics.e x = setcolor(RED) is identical to ... x = global:setcolor(global:RED) ** Only "global" symbols will be in named namespaces. The scope of non-global symbols is already well defined and acceptable. ** Control of the namespace ID must be with the code doing the referencing. The alternative means that inadvertent clashes could still occur. For example if RDS creates a namespace "rds" and places all its routines in there, then I come along and create a function with the same name as an RDS one and just happen to put mine into "rds" too, a name clash will occur. e.g.. -- Syntax that forces a namespace for each include file -- include graphics.e as rds include get.e as rds or even -- Syntax that bunches multiple include files into a namespace -- namespace rds include graphics.e include get.e end namespace I'd actually like both these syntaxes to be availble. ** The namespace ID must be independent of the include file name. File names just do not make good namespace IDs. There are also characters allowed in file names that are not allowed in Euphoria names. e.g. include file.abc.e -- Allowed as a file name, but not as a symbol name. e.g.. include graphics.e as rds -- and not default to "graphics" ** Any symbol can be prefixed with a namespace reference. I prefer a colon character as the delimiter but this is not so important. e.g.. x = rds:setcolor(rds:RED) If that symbol does not exist in the named namespace an error "undefined symbol" occurs. ** If the namespace id in a prefix or a namespace list is not explicitly defined by the current file, an error occurs "unknown namespace" This is another way of saying that defined namespaces are scoped to the file they are defined in. -- fileA.e ---- include graphics.e as rds -- fileB.e --- include fileA.e x = rds:setcolor(rds:RED) -- Error: 'rds' not defined in fileB.e using rds --Error: 'rds' not defined. x = setcolor(RED) end using but this IS okay... -- fileB.e --- include fileA.e include graphics.e as rds x = rds:setcolor(rds:RED) using rds x = setcolor(RED) end using ** A global symbol can only be defined once in the same file. This is current semantics and no need to change. ** A symbol can only exist in a namespace once. Otherwise, how would Euphoria know which symbol you are referring to? ** Attempting to put a symbol into a namespace and that symbol already exists there, is not an error if both symbols come from the same file. This is consistent with current Euphoria. e.g.. -- Current Euphoria -- include graphics.e include graphics.e -- This is not an error, it is ignored. -- Proposed Euphoria -- include graphics.e as rds include graphics.e as rds -- This is not an error, it is ignored. ** A reference to the same symbol can exist in multiple namespaces. This situation is bound to happen sometimes and I can't think of a good reason why it should be an error. Also, with my "insert" idea described further down, this is more likely to happen. e.g.. include graphics.e as rds include graphics.e as dos include graphics.e as std x = std:setcolor(std:RED) y = dos:setcolor(dos:RED) -- Exactly the same objects referred to. ** The coder can specify a namespace list and its scope. The default namespace list is "global". This can save a lot of coding and extra reading. It also makes "fixing" up pre-2.3 code easier. e.g.. include graphics.e as rds include win32lib.ew as w32 include colornames.e as clr using rds,w32 x = setcolor(BrightRed) -- setcolor from "rds" even though "w32" might have one, -- BrightRed from "w32" even though "clr" might have one. y = setcolor(Blue) z = setcolor(White) end using The alternative, and also legal syntax would be... include graphics.e as rds include win32lib.ew as w32 include colornames.e as clr x = rds:setcolor(w32:BrightRed) y = rds:setcolor(w32:Blue) z = rds:setcolor(w32:White) ** Namespace lists can be nested. e.g.. include trial\graphics.e as rds include graphics.e include win32lib.ew as w32 using rds x = setcolor(RED) -- from "trial\graphics.e" using w32 y = setcolor(BrightRed) -- from "win32lib.ew" z = global:setcolor(global:RED) -- from "graphics.e" end using setText(myfld1, x) setText(myfld2, y) setText(myfld3, z) end using ** The search path for symbol resolution should be... 1) The namespace prefix, if present, on a symbol. 2) The current function or procedure 3) The current file 4) The namespace list currently in scope. (Remember that global is the default) Now even with the stuff covered above there are at least three real situations that will still be problematic, unreasonably so in my opinion. ============= Situation #1 ============= How can I have a number of include files share access to some symbols that I do not want to share with other files? David Cuny highlighted this problem. It occurs when breaking up a larger include file (e.g.. win32lib.ew) into a number of smaller files. While the (local) routines where all in same file they could refer to each other as "private" routines, specifically not available to coders using the library. However, once you split them into separate files, the local routines are now hidden. To make them available to each other, via a global specifier, also exposes them to other coders. C++ kind of got around this situation by inventing "friend" classes. This is just a complex kludge that is not really helping. A simpler solution is the have Euphoria pretend that these files are still in the same master file, then all the normal semantics apply. My idea is to introduce a "insert" command to be used instead on "include". Any file "inserted" would be treated by the Euphoria interpreter as if its contents where actually at the line of insertion. e.g.. --- fileA.ew -- x = funcA() --- win32lib.ew -- function funcA() return ... end function insert fileA.ew this would still keep "funcA" private to these files. ================== Situation #2 ================== -- fileA.e -- global constant WM_NOTIFY = 15 -- fileB.e -- global constant WM_NOTIFY = 15 -- fileC.ex -- include fileA.e include fileB.e This currently causes a problem of duplicate definitions. The new rules outlined above don't help either. But why should it be a problem? This type of situation comes up when we have to define constants that come from other environments. In my case, there are thousands of symbols defined by Microsoft for use with Windows programming. It makes reasonable sense that everybody should stick to the same symbol names defined by Microsoft rather than coming up with their own variants. But that is where Euphoria has problems. If Bill writes an include file that needs some Windows constants and Jill writes another such include file all is well. But when Derek tries to include those files written by Bill and Jill into my code, Euphoria barfs if it just happens that the same Windows constants were defined (as global) in two or more of these files. I'd like Euphoria's behavior to change such that when a duplicate constant (local or global) is defined, it is only an error if that has a different value. Thus ... -- fileA.e -- global constant WM_NOTIFY = 15 -- fileB.e -- global constant WM_NOTIFY = 14 -- fileC.ex -- include fileA.e include fileB.e would cause a problem because the same constant is being given different values. And while we're at it, how about allowing constants to be defined inside a function/procedure. It would increase Euphoria's orthoganiality! e.g.. procedure procA(sequence CustomerRecord) sequence lAddress constant Street = 1 , PostalCode = 2 if length(CustomerRecord) = 7 then lAddress = CustomerRecord[3 .. 4] else lAddress = {CustomerRecord[5], CustomerRecord[3]} end if if length(lAddress[Street]) = 0 then .... end procedure =================== Situation #3 =================== Another way of avoiding global name clashes is to only include the "correct" file. This might be arranged like ... object GuiSys GuiSys = lower(getenv("GUISYSTEM")) if equal(GuiSys, "motif") then include motif\gui.e elsif equal(GuiSys, "os2") then include os2\gui.e elsif equal(GuiSys, "playstation2") then include playstation\2\gui.e elsif equal(GuiSys, "windows") then include windows\gui.e elsif equal(GuiSys, "amiga") then include amiga\gui.e elsif equal(GuiSys, "palm") then include palm\gui.e else abort(0) -- No GUI system defined. end if . . . setText(myFld, "Hello world") -- picks up the "correct" version of setText(). In this case, the interpreter would only "include" the file if the condition was met and would not even look into the other files. Another simpler example: if atom(getenv("DEBUGGING")) then include machine.e else include safe.e end if And I haven't even touched on all the other neat "wishlist" items that I have for Euphoria. ----------- cheers, Derek Parnell Senior Design Engineer Global Technology Australasia Ltd dparnell at glotec.com.au --------------------- confidential information intended solely for the use of the individual or entity to whom they are addressed. If you are not the intended recipient of this message you are hereby notified that any use, dissemination, distribution or reproduction of this message is prohibited. If you have received this message in error please notify the sender immediately. Any views expressed in this message are those of the individual sender and may not necessarily reflect the views of Global Technology Australasia Limited.