[M3devel] Implementing the "Singleton pattern" in Modula-3
Tony Hosking
hosking at cs.purdue.edu
Tue Apr 7 08:15:50 CEST 2009
Yes, I admit this is cumbersome:
GENERIC MODULE Singleton(Super, Impl);
REVEAL T = Super BRANDED OBJECT OVERRIDES m := Impl.m END;
BEGIN
t := NEW(T);
END Singleton.
On 7 Apr 2009, at 15:39, Mika Nystrom wrote:
>
> Hmm, well ... so in C++ and Smalltalk you can accomplish something
> along these lines:
>
> INTERFACE Singleton;
>
> TYPE
> T = SomeSuperTypeIReallyWantToSubClass OBJECT METHODS
> m();
> END;
>
> END Singleton.
>
> (****************************************)
>
> MODULE Client;
>
> BEGIN
>
> theSingleton := NEW(Singleton.T, m := MyM);
> theSingleton2 := NEW(Singleton.T, m := MyM);
>
> END Client.
>
> And by some magic involving overriding "new methods", you are
> guaranteed that theSingleton and theSingleton2 are the same object...
>
> In Modula-3 you can do something similar by providing an appropriate
> init, but this can still get a bit hairy since the client can
> override init and then you're back where you started.
>
> As I said this is really quite a minor issue but it has annoyed me
> a few times that I have to be very careful about allocating only one
> of something, especially when it might be allocated from lots of
> places and you want to make sure only one ever gets allocated in a
> single
> program execution. (E.g., various types of "registries"...)
>
> Mika
>
> Tony Hosking writes:
>> On 7 Apr 2009, at 11:18, Mika Nystrom wrote:
>>
>>> Well I think the problem is with this "is known to be an object
>>> type"...
>>
>> But if the opaque type T is T<:REFANY and the only revelation is
>> branded and private to a module (not exported by its interfaces) then
>> no-one can determine that it is an object type without forging the
>> BRAND.
>>
>>> You may in fact want to override the methods of the singleton, but
>>> ensure that only one ever gets instantiated.
>>>
>>> It's a minor problem, but it does sometimes bother me that it is
>>> difficult to prevent clients from NEWing pretty much anything they
>>> like.
>>>
>>> By the way, the "Modula-3 Type System" paper also talks about a
>>> nifty way to do narrowing very quickly (page 10, last paragraph).
>>> I remember this is occasionally problematic. (RTType.IsSubtype
>>> isn't great.) They speak of keeping an array of supertypes
>>> for each type, and recording the "depth" (distance from REFANY)
>>> of every type in, I guess, RT0.TypeDefn. This seems moderately
>>> space-efficient (better than a 2D array of all the typecodes),
>>> and very fast.
>>>
>>> Mika
>>>
>>> Tony Hosking writes:
>>>> I would exploit opaque types and branding. If T is opaque then you
>>>> cannot instantiate T except in scopes where T's concrete type has
>>>> been
>>>> revealed or is known to be an object type. Thus, you could
>>>> define an
>>>> interface:
>>>>
>>>> GENERIC INTERFACE Singleton(Rep);
>>>> TYPE T <: REFANY;
>>>> VAR t: T;
>>>> END Singleton.
>>>>
>>>> GENERIC MODULE Singleton(Rep);
>>>> REVEAL T = BRANDED REF Rep.T;
>>>> BEGIN
>>>> t := NEW(T);
>>>> END Singleton.
>>>>
>>>> where Rep defines the type T. You could do similar for object
>>>> types.
>>>>
>>>>
>>>> On 7 Apr 2009, at 06:31, Mika Nystrom wrote:
>>>>
>>>>>
>>>>> Tony, Jay, you two seem to be most knowledgeable about this.
>>>>>
>>>>> Let's say I wanted to implement the "singleton pattern" in
>>>>> Modula-3. At present there seems to be no way of doing this.
>>>>>
>>>>> What about changing RTAllocator.callback as follows (or adding
>>>>> another callback):
>>>>>
>>>>> VAR callback : PROCEDURE(r : REFANY) : REFANY := NIL;
>>>>>
>>>>> (* If non-NIL, the allocator calls "callback(r)" just before
>>>>> returning
>>>>> a new traced reference "r" and instead returns the result of
>>>>> callback(r).
>>>>> See "RTAllocStats" for an example client. It is a checked runtime
>>>>> error
>>>>> for the typecode of r to differ from the typecode of the return
>>>>> value of
>>>>> callback(r). *)
>>>>>
>>>>> Mika
>>>>>
>>>>>
>>>>>
More information about the M3devel
mailing list