Selector Namespaces (Was: Behaviors vs Modules)

David Simmons David.Simmons at smallscript.com
Sat Feb 23 20:26:46 UTC 2002


> -----Original Message-----
> From: squeak-dev-admin at lists.squeakfoundation.org [mailto:squeak-dev-
> admin at lists.squeakfoundation.org] On Behalf Of Jecel Assumpcao Jr
> Sent: Friday, February 22, 2002 2:05 PM
> To: squeak-dev at lists.squeakfoundation.org
> Subject: Re: Behaviors vs Modules
> 
> On Friday 22 February 2002 23:38, Anthony Hannan wrote:
> > - A Smalltalk expression now knows, via the selectors it calls,
which
> > protocols and corresponding default behaviors it depends on.  So
> > starting from a Smalltalk expression we can trace selectors and
> > methods recursively and load all default behaviors that will be
> > needed.
> 
> Two slight problems:
> 
>   1) #perform:
> 
>   2) if #foo is used in only one protocol in your system, you don't
> have to say which one that is, right? Now imagine that you send your
> module to me and I load it into my system which also has a #foo in
only
> one protocol, but it happens to be a different protocol from yours!
> 
> But I do very much like the idea of protocol objects, specially if
they
> are used as factories instead of direct class references. It would be
> great if an application simply said that it needed a new object with
> the socket protocol and it got a fully compatible one that happened to
> be the user's favorite. Think about asking for "a web browser" instead
> of "IE 5.5 SP2". Direct class references in the code are a case of
> overspecification and will cause us lots of problems in the future, so
> I don't think a new syntax for making them easy (see other thread)
will
> help.

In SmallScript, the #foo declaration is (implicitly) a
#CallSiteClass.private.foo selector. Therefore, the selector indicates
the context from which the call should be bound. Where, by default, the
context is set to the callsite's method's dictionary [note: there is a
subtlety I am skipping for simplicity here].

In SmallScript, interfaces are classes. Classes are namespaces.
Selectors (symbols) are always associated with a namespace. I.e., a
symbol consists of a selector familyName, a selector scope (namespace),
and a family-group-selector (for var-args).

So we can write:

    someObject #SomeScope.familyName.

OR

    someObject perform(#SomeScope.familyName).
    someObject perform: #SomeScope.familyName.

OR
    |selector| := #SomeScope.familyName.
    someObject perform(selector).
    someObject perform: selector.

Now, since an interface is a class, and classes are namespaces, we can
write: (this example is intentionally complicated to illustrate
subtleties)

    Library name: TestModule.
    Interface name: MyProtocolA
    {
        Method [<$interface-only> 
        action
            stdout cr << 'MyProtocolA Public Action Ran'.
            self action.
        ]
        Method scope: family [ "aka scope: MyProtocolA"
        action
            stdout cr << 'MyProtocolA Protected/Family Action Ran'.
        ]
    }
    Interface name: MyProtocolB
    {
        Method [ 
        action
            stdout cr << 'MyProtocolB Public Action Ran'.
            self action.
        ]
        Method scope: family [ "aka scope: MyProtocolB"
        action
            stdout cr << 'MyProtocolB Protected/Family Action Ran'.
        ]
    }
    Class name: Foo 
        implements: MyProtocolA, MyProtocolB
        imports: MyProtocolB
    {
        Method scope: module [ 
        action
            stdout cr << 'My Public Action Ran'.
        ]

        Method scope: Foo.class [ "aka scope: Foo.private" 
        action
            stdout cr << 'My Private Action Ran'.
        ]

        Method [
        test
            "" Runs private version
            self action.  "effectively #Foo.action"

            stdout cr << '---'.
            
            "" Runs MyProtocolA version
            self #MyProtocolA.action.

            stdout cr << '---'.
            
            "" Runs MyProtocolB version
            self #MyProtocolB.action.

            |ifaceA| := self as: MyProtocolA.
            |ifaceB| := self as: MyProtocolB.

            stdout cr << '---'.
            
            "" Runs MyProtocolA version
            ifaceA action.

            stdout cr << '---'.
            
            "" Runs MyProtocolB version
            ifaceB action.
        ]
    }
    
    [
        |foo| := Foo new.
        stdout cr << foo.class.supertypes.
        stdout cr << foo.class.superscopes.
        stdout cr << '---'.
        foo #public.action.
        stdout cr << '---'.
        foo action. 
        stdout cr << '---'.
        foo test.
    ]

This example is intentionally complicated to illustrate the fine
(subtleties) that are involved. Examine the output carefully to see how
various declarations affect the binding sequences.

Keep in mind that it is a requirement that one be able to make such fine
distinctions (when they matter). 

Also it is most important to keep in mind, that in the
general/standard-coding case, a developer does not need to think about
these issues. For example, declaration of "scope:" or qualifying a
message (or perform selector) with a scope is unnecessary. 

NOTE: I also did not discuss/present any of the strong name issues.
#{SomePath:uuid}, etc.

Here is the output from running the above script:

	{Foo, MyProtocolA, MyProtocolB, Interface, Mixin, Object, nil}
	{Foo.metaclass, Foo, MyProtocolB, DotNet, TestModule, Object,
AOS, nil}
	---
	MyProtocolB Public Action Ran
	MyProtocolB Protected/Family Action Ran
	---
	My Module Scoped Action Ran
	---
	My Private Action Ran
	---
	MyProtocolA Protected/Family Action Ran
	---
	MyProtocolB Protected/Family Action Ran
	---
	MyProtocolA Public Action Ran
	MyProtocolA Protected/Family Action Ran
	---
	MyProtocolB Protected/Family Action Ran    

-- Dave S. [SmallScript Corp]

SmallScript for the AOS & .NET Platforms
David.Simmons at SmallScript.com | http://www.smallscript.org

> 
> -- Jecel




More information about the Squeak-dev mailing list