"Doc O'Leary" <droleary / subsume.com> wrote in message news:251120011105195454%droleary / subsume.com... > In article <Su_L7.44988$RG1.23138548 / news1.rdc1.sfba.home.com>, David > Simmons <david.simmons / smallscript.com> wrote: > > [...snip...] I already responded the original problem presented in Waldemar Horwat's JavaScript 2.0 slides. Let me demonstrate a different use of selector namespaces which cannot be solved through versioning. In this example I will also mention the use of indirect use of concrete interfaces for providing implementation behavior. ---- Problem: Two unrelated Smalltalk developers are creating modules for handling monetary units [grossly simplified]. As each independently thinks about their design, one of the problems they encounter is that of converting string representations into money objects. They both decide that it would be most appropriate (from a design usage point of view) to enable <String> objects to handle conversions to monetary unit objects. I.e., they would like to enable expressions like: '$50.00' asMoney. "possibly #asDollars" For whatever reasons, they have both decided that some class (which they did not write/own -- in this case <String>), is the most appropriate place to implement a method (which they will write and which will owned by their projects/modules). So, naively they write: ---------------------------- Party One's Monetary Package ---------------------------- <Project name: USCurrency> Class name: Dollars { ... Method [ + <Dollars> aValue ... ] } Method class: String [ asMoney ... ] </Project> ---------------------------- Party Two's Monetary Package ---------------------------- <Project name: CanadianCurrency> Class name: Dollars { ... Method [ + <Dollars> aValue ... ] } Method class: String [ asMoney ... ] </Project> ==== All is well, they've versioned, built-unit tests, etc. They even develop sophisticated tools and release them to 3rd parties for consumption. Any string can be sent the message #asMoney and it does the "right" thing in the unit tests, etc. A third party integrator comes along and has the requirement of writing a program that requires both US and Canadian currency operations. They are given the requirement that they must use the party-one's and party-two's packages (rather than writing their own). They may even be in the uncomfortable but likely position that they do not have source to the packages [as would commonly be the case for static languages and platforms based on binary component approaches like .NET]. Now we have a serious problem. Both parties have made implementations that appear to conflict. First, without namespaces the class name <Dollars> will collide. Even with namespaces, the #asMoney methods on <String>, will conflict. Since the #asMoney methods (minimally) yield unrelated return types. I.e., one party returns a unit of Candian <Dollars> and the other party returns a unit of US <Dollars>. --- Arguably, at this point, one would say namespaces would solve the <Dollars> problem. And there is no way they should be modifying the <String> class they don't own. Heck let's add language features to seal and finalize things so they can't do this even in dynamic languages that enable runtime schema changes. But is that really the right answer? After all, if the they had owned/created the <String> class we would say there was no problem with adding a monetary conversion method to it [although some people might think, hey, String is a special class and should not be touched -- but, think again, why is it special?]. In general, as an object-oriented approach we encourage this type of design decision as a well placed responsibility. So why is it wrong here? Is it because it is inherently wrong, or is it because implementation constraints and existing language semantics of static-languages (and to some degree dynamic languages) have creeped in to provide rules we should question? But, if we could offer a uniform technique that allowed one to "encapsulate" changes to our own code base and yet share our implementation with others, then it would be fine. This is a feature that has been understood in various non-oo imperative/function language module systems. So why not allow it in class-based dynamic language OO systems... Again, selector-namespaces allow us to do just that. If we could "scope" the #asMoney methods provided by party-one and party-two then we could provide this capability. [add concrete Interfaces to this and we have an even better general solution]. ==== Revising the code we write it as: ---------------------------- Party One's Monetary Package ---------------------------- <Project name: USCurrency> Class name: Dollars { ... Method [ + <Dollars> aValue ... ] } Method class: String scope: USCurrency [ asMoney ... ] </Project> ---------------------------- Party Two's Monetary Package ---------------------------- <Project name: CanadianCurrency> Class name: Dollars { ... Method [ + <Dollars> aValue ... ] } Method class: String scope: CandianCurrency [ asMoney ... ] </Project> ==== We also note that in a typical Smalltalk IDE, this scoping would be provided by default by the tools for any methods we wrote onto classes which were not contained within the project. Now our 3rd party, who needed to implement a single application that used both libraries can do so without conflicts. The <Dollars> classes are distinguished through namespaces (remembering that modules are classes and classes are namespaces). Both classes <USCurrency.Dollars> and <CanadianCurrency.Dollars> comfortably exist. The #asMoney methods both reside in <String> and have their method characteristics set to: Party one's USCurrency version of <String> #asMoney has: the selector #USCurrency.asMoney; and the module attribute set to <USCurrency> to enable versioned unloading/reloading of the module. Party two's CanadianCurrency version of <String> #asMoney has: the selector #CanadianCurrency.asMoney; and the module attribute set to <CanadianCurrency> to enable versioned unloading/reloading of the module. All the code in party one's tools will have specified access to <USCurrency> as a namespace, and will not have access to party two's <CanadianCurrency> namespace (because they did not know about it). Thus any of party one's code will always bind to the #USCurrency.asMoney method in <String>. The same can now be said for party two's code. All the code in party two's tools will have specified access to <CanadianCurrency> as a namespace, and will not have access to party one's <USCurrency> namespace (because they did not know about it). Thus any of party one's code will always bind to the #CanadianCurrency.asMoney method in <String>. ---- If our 3rd party wants to write code that references both libraries they will need to specify the particular namespaces they are referring to. Let's assume they decide to write code to convert both Canadian and US dollars. So they would like to have methods called #asCanadianDollars and #asUSDollars, but they want to be insulated from implementation changes provided by party one and/or party two's version updates. They might write: (noting that a typical Smalltalk IDE would provide the scoping attribute by default for methods on external classes) <Project name: 3rdParty> Requires module: USCurrency. "versioning prequisites elided" Requires module: CanadianCurrency. "versioning prequisites elided" Method class: String scope: 3rdParty [ asCanadianDollars ^self #CanadianCurrency.asMoney ] Method class: String scope: 3rdParty [ asUSDollars ^self #USCurrency.asMoney ] .... other project code and classes here... </Project> =================== The interfaces technique would be written slightly differently (and I am not really giving it due consideration in picking names for this grossly simplified example, but OTOH I am will not subsequently be particularly constrained by my up-front decisions either). Here it is, but since I am running out of time I won't particularly explain it now. I'll leave that to the reader and perhaps subsequent posts. ---------------------------- Party One's Monetary Package ---------------------------- <Project name: USCurrency> Class name: Dollars { ... Method [ + <Dollars> aValue ... ] } Interface name: IMonetaryConversion default-scope: USCurrency { Method [ asMoney ... ] ... other methods here... } Function [ Initialize String addInterface: IMonetaryConversion. ] Function [ Finalize String removeInterface: IMonetaryConversion. ] </Project> -- Dave S. [www.smallscript.org]