Another Singleton Pattern... 
Author Message
 Another Singleton Pattern...

I guess I'll have to take the minority position and state  that I'm
a bit hesitant to use make as the generic to return the singleton object.

1. The DRM allows make to return a previously allocated instance.
   I read that as allowing allocation from a pool ( if doable and
   reflective of the results that the normal protocol would provide).
   If make is suppose to be allowed to create aliases then the DRM
  (version 2) should state so explicitly.   In short  factory refurbished
  objects are fine....  it is something different to say that everyone
  has to share the same object.

  [ There is a difference between allowing the runtime environment
implementors
    to use pooled allocation and allowing pooled allocation by user defined
    methods added to make.  In the latter case, an argument can be made
    that another generic function should be used since it must be paired
    with some sort of "free".  Different generics for different protocols. ]

  I think the expectation in a language with reference based semantics
  is that "make" creates a new object.

    foo := make ( <foo-class> )
    bar := make ( <foo-class> )

    foo.slot1 := 23 ;

  The expectation is that  foo changes;  not foo and perhaps bar.  If I can't
  trust make not to create aliases I'd have to do explicit
  alias detection.  Shades of  C and testing the results of malloc. Sometimes
  it gives you something useful and sometimes it doesn't.

  The argument of "it is a singleton class, it is suppose to do that" works
  equally well for "don't use make because it is a singleton class".  I
suspect
  the reason that make is used as the constructor method is because the
  "clueless" might just use it by mistake.   If they are using it
  by mistake how likely are they going to treat "make" as if it can
  create aliases?????

  [ If the designers do intend for "make" to produce aliases I would like
    to see some sort of predicate you can apply to a class to tell you
    that its make acted in this non intuitive fashion.

    I know this wouldn't be conclusive but at least you might take
    precautions for folks that did correctly follow this approach. ]

2. On a similar a similar note:

     foo :=  make ( <foo-class>  ,  slot1: 23   ,  slot2: 53 )

     bar :=  make ( <foo-class>  ,  slot1: 1111 ,   slot2: 22222)

   After the above, I think the reasonable person would expect both of the
   following to return "true".

        foo.slot1 == 23 ;

        bar.slot1 == 1111;

   However supposedly only one of these is suppose to be the case. Which one?
   It depends.  There is no "correct" answer.   The DRM clearly states that
   these init-args are suppose to utilized by make to set the object slots
   to the specified values.  That would make the second override the first.
   The counter argument is that the first allocates but the second doesn't.
   That  is easy to see above.  Separate them into different modules in
   different libraries and would that be equally as obvious?

   For the suggested implementations, the first is true and the second is
false.    

   Again this seems to be a violation of the instantiation/initialization
   protocol put forward in the DRM.  Again this isn't a problem if DRM v 2.0
   would explicitly state that these are only mild suggestions as to
   what should happen at initialization.

   My reading is that intent of the DRM is that other "additions" to the
   make/initialization methods exhibit roughly the same behavior as outlined
   with whatever "enhancements" the class needs.   If  make/initilize do
   something significantly different then perhaps these methods should
   not be added to the generic functions with these names.

   This is a general design decision with generic functions.  How far can
   you bastardize the interface/behaviour before you should start thinking
   about creating a new generic instead of twisting a preexisting one
   to do something else.

   Generics are shared in a common namespace.  This isn't like C++ where
   each class has its own namespace.  One name cannot be all things to
   all clients.

   [ I think the presented solution would be better if it disallowed
     the specification of initialization arguments.   The semantics of
     what "should" happen are ambiguous.  However, I'm not adverse
     to make invoking an error. ]

3. I wondered about this so yesterday when I happened to be in a bookstore
   I paged through the Smalltalk Companion book.   They outline a pattern
   similar to solution I'll present at the end of this message.   They
suggest
   that there is usage of the Singleton pattern that isn't persistent.
   Namely there is only one instance at a time. However, this is not
   same instance throughout the course of the program ( or every session).

   The solution discussed so far would make it difficult to transition from
   one instance to another.  Exactly how to you create a new instance
   now that you have taken away make?

   [ The upside of deviating dramatically from the DRM initialization
     "standard modus operandi" for make is that no matter how misused; make
     will never return an error.   The protected C++ constructors cause
     a compile time error if used in the improper namespace.   With
     more "flat namespace" dynamic languages you may have to wait until
     runtime to get an error message.   That's problematic in the case

              if  (  once-in-a-blue-moon (... ) )
                    make ( <foo> );
              end if;

     if this branch doesn't get exercised in testing and shipped in
     production code then the users may encounter this first. ]

So what is better....

The alternative I've been thinking of for a while would utilize the
initialization key value pairs to "authorize" make being invoked by
code "private" to the singleton class while invoking a runtime error if
used "improperly".

This stuff should be packaged up in a macro ( or a pair of macros... I
just didn't have time to do that).    The "table of class=>sole-object"
approach could also be used instead of storing on a variable.

// these two should be in the same module the macro(s) are exported from
//  but not explicitly exported. There are only for use by the macro.

// this just makes it more difficult to "fake" authorization... not perfect.

define constant  *singleton-init-passwd* = "avert thine eyes";

define function  authorized-make? ( init-key-pairs :: <sequence> )
 => (  validity :: <boolean> )
  let seq-size :: <integer> = size ( init-key-pairs );
  let validity :: <boolean> = #f ;
     for ( i from 0 below seq-size by 2 )
        if (  (init-key-pairs[i] == #"for-internal-singleton-use-only") &
              (init-key-pairs[i + 1] == *singleton-init-passwd* ) )
          validity := #t
        end if;
     end for;
  validity;
end function authorized-make?;

//  export  singleton-class-definer macro for intance.

.... in another library/module....

//
//  I'd be inclined disallowed initialized keywords required or otherwise
//   out of this macro.  Doing so would involve writing a more complicated
//   macro but I think it might help.
//
//  define singleton-class <base1>  ( <singleton-class> )
//      slot slot1 ,      init-keyword:  slot1:
//  end  singleton-class ;

//   could expand into the following code......

// not sure if this is needed or not...
  define constant *<base1>-super-check* =  ...some voodoo code to check that
                                             this is in <singleton-class>
                                             hierarchy...

  define class <base1>  ( <singleton-class> )
        slot slot1 , init-keyword: slot1:
  end singleton-class ;

  define variable *<base1>-sole-object* :: false-or ( <base1> ) = #f

   define  method make  ( class == <base1> , #rest  init-key-pairs , #key)
     => ( result  :: <base1> ) ;
   if ( authorized-make? ( init-key-pairs) )
      next-method();
   else
       error ("Attempt to call make on a singleton class");
   end if;

  // don't allow anyone interfere later.
  define sealed domain   make ( class == <base1> ) ;
  define sealed domain   initialize ( class == <base1> ) ;

  define method  get-sole-object ( class == <base1> )
      => ( result :: <base1>);
    *<base1>-sole-object*
    | begin
       *<base1>-sole-object* :=  make ( <base1> ,
                for-internal-singleton-use-only: *singleton-init-passwd* );
      end;
  end method get-sole-object;  

With the above  definitions:

   b1 :=  get-sole-object ( <base1> ) ;   // works.  

   b2 :=  make ( <base1> )  ;             // runtime error.

   b3 :=  make ( <base1> , slot1: 23 ) ;  //  runtime error.

   b4 :=  make ( <base1> , for-internal-singleton-use-only:
"avert-thine-eyes");

 This last one works.  However, it is hard to believe that writer of that
 line doesn't know that they are purposely using a "back door".  If a
 macro is the primary user of the "back door" then the users need not
 be directly involved.

 Can a programmer who believes they have the "superior intellect" by-pass
 the safeties and shoot themselves in the foot with a large caliber weapon?
 Yes.   I don't spend too much time worrying about those folks. :-)

 [ I think a dylan-lint program might be able to identify the second and
   third as being suspicious.  However, that would require notices that
   the "authentication" init arg is missing. ]

  I did some testing and HDylan doesn't mind the "extra" init arg in the
  key pairs sequence.   The authorized-make?  function could return a
  boolean and a copy of the sequence without the key/value pair in it.
  However, I don't think that is necessary.

P.S.  The smalltalk pattern seems to be invoke the "super" ( or more
     "basic" version) of make and  set the "new" method to always
     return an error.  The above allows to paths through make. One
...

read more »



Thu, 20 Sep 2001 04:00:00 GMT  
 Another Singleton Pattern...

I shall add my voice to the chorus of those who feel that returning
singleton objects is a legitimate use of make.  If you want to be
secure in the knowledge that no other programmers will circumvent
you mechanism for returning the distinguished object, then make is
the place for this functionality.  It is the sole gateway at which
will stand your angel with the flaming sword to prevent undistinguished
simulacra of the designated, blessed singleton object from escaping
and infecting your unsuspecting application.


Quote:

>I guess I'll have to take the minority position and state  that I'm
>a bit hesitant to use make as the generic to return the singleton object.

>1. The DRM allows make to return a previously allocated instance.
>   I read that as allowing allocation from a pool ( if doable and
>   reflective of the results that the normal protocol would provide).
>   If make is suppose to be allowed to create aliases then the DRM
>  (version 2) should state so explicitly.   In short  factory refurbished
>  objects are fine....  it is something different to say that everyone
>  has to share the same object.

>  [ There is a difference between allowing the runtime environment
>implementors
>    to use pooled allocation and allowing pooled allocation by user defined
>    methods added to make.  In the latter case, an argument can be made
>    that another generic function should be used since it must be paired
>    with some sort of "free".  Different generics for different protocols. ]

>  I think the expectation in a language with reference based semantics
>  is that "make" creates a new object.

>    foo := make ( <foo-class> )
>    bar := make ( <foo-class> )

>    foo.slot1 := 23 ;

>  The expectation is that  foo changes;  not foo and perhaps bar.  If I can't
>  trust make not to create aliases I'd have to do explicit
>  alias detection.  Shades of  C and testing the results of malloc. Sometimes
>  it gives you something useful and sometimes it doesn't.

>  The argument of "it is a singleton class, it is suppose to do that" works
>  equally well for "don't use make because it is a singleton class".  I
>suspect
>  the reason that make is used as the constructor method is because the
>  "clueless" might just use it by mistake.   If they are using it
>  by mistake how likely are they going to treat "make" as if it can
>  create aliases?????

>  [ If the designers do intend for "make" to produce aliases I would like
>    to see some sort of predicate you can apply to a class to tell you
>    that its make acted in this non intuitive fashion.

>    I know this wouldn't be conclusive but at least you might take
>    precautions for folks that did correctly follow this approach. ]

>2. On a similar a similar note:

>     foo :=  make ( <foo-class>  ,  slot1: 23   ,  slot2: 53 )

>     bar :=  make ( <foo-class>  ,  slot1: 1111 ,   slot2: 22222)

>   After the above, I think the reasonable person would expect both of the
>   following to return "true".

>        foo.slot1 == 23 ;

>        bar.slot1 == 1111;

>   However supposedly only one of these is suppose to be the case. Which one?
>   It depends.  There is no "correct" answer.   The DRM clearly states that
>   these init-args are suppose to utilized by make to set the object slots
>   to the specified values.  That would make the second override the first.
>   The counter argument is that the first allocates but the second doesn't.
>   That  is easy to see above.  Separate them into different modules in
>   different libraries and would that be equally as obvious?

>   For the suggested implementations, the first is true and the second is
>false.    

>   Again this seems to be a violation of the instantiation/initialization
>   protocol put forward in the DRM.  Again this isn't a problem if DRM v 2.0
>   would explicitly state that these are only mild suggestions as to
>   what should happen at initialization.

>   My reading is that intent of the DRM is that other "additions" to the
>   make/initialization methods exhibit roughly the same behavior as outlined
>   with whatever "enhancements" the class needs.   If  make/initilize do
>   something significantly different then perhaps these methods should
>   not be added to the generic functions with these names.

>   This is a general design decision with generic functions.  How far can
>   you bastardize the interface/behaviour before you should start thinking
>   about creating a new generic instead of twisting a preexisting one
>   to do something else.

>   Generics are shared in a common namespace.  This isn't like C++ where
>   each class has its own namespace.  One name cannot be all things to
>   all clients.

>   [ I think the presented solution would be better if it disallowed
>     the specification of initialization arguments.   The semantics of
>     what "should" happen are ambiguous.  However, I'm not adverse
>     to make invoking an error. ]

>3. I wondered about this so yesterday when I happened to be in a bookstore
>   I paged through the Smalltalk Companion book.   They outline a pattern
>   similar to solution I'll present at the end of this message.   They
>suggest
>   that there is usage of the Singleton pattern that isn't persistent.
>   Namely there is only one instance at a time. However, this is not
>   same instance throughout the course of the program ( or every session).

>   The solution discussed so far would make it difficult to transition from
>   one instance to another.  Exactly how to you create a new instance
>   now that you have taken away make?

>   [ The upside of deviating dramatically from the DRM initialization
>     "standard modus operandi" for make is that no matter how misused; make
>     will never return an error.   The protected C++ constructors cause
>     a compile time error if used in the improper namespace.   With
>     more "flat namespace" dynamic languages you may have to wait until
>     runtime to get an error message.   That's problematic in the case

>              if  (  once-in-a-blue-moon (... ) )
>                    make ( <foo> );
>              end if;

>     if this branch doesn't get exercised in testing and shipped in
>     production code then the users may encounter this first. ]

>So what is better....

>The alternative I've been thinking of for a while would utilize the
>initialization key value pairs to "authorize" make being invoked by
>code "private" to the singleton class while invoking a runtime error if
>used "improperly".

>This stuff should be packaged up in a macro ( or a pair of macros... I
>just didn't have time to do that).    The "table of class=>sole-object"
>approach could also be used instead of storing on a variable.

>// these two should be in the same module the macro(s) are exported from
>//  but not explicitly exported. There are only for use by the macro.

>// this just makes it more difficult to "fake" authorization... not perfect.

>define constant  *singleton-init-passwd* = "avert thine eyes";

>define function  authorized-make? ( init-key-pairs :: <sequence> )
> => (  validity :: <boolean> )
>  let seq-size :: <integer> = size ( init-key-pairs );
>  let validity :: <boolean> = #f ;
>     for ( i from 0 below seq-size by 2 )
>        if (  (init-key-pairs[i] == #"for-internal-singleton-use-only") &
>              (init-key-pairs[i + 1] == *singleton-init-passwd* ) )
>          validity := #t
>        end if;
>     end for;
>  validity;
>end function authorized-make?;

>//  export  singleton-class-definer macro for intance.

>.... in another library/module....

>//
>//  I'd be inclined disallowed initialized keywords required or otherwise
>//   out of this macro.  Doing so would involve writing a more complicated
>//   macro but I think it might help.
>//
>//  define singleton-class <base1>  ( <singleton-class> )
>//      slot slot1 ,      init-keyword:  slot1:
>//  end  singleton-class ;

>//   could expand into the following code......

>// not sure if this is needed or not...
>  define constant *<base1>-super-check* =  ...some voodoo code to check that
>                                             this is in <singleton-class>
>                                             hierarchy...

>  define class <base1>  ( <singleton-class> )
>        slot slot1 , init-keyword: slot1:
>  end singleton-class ;

>  define variable *<base1>-sole-object* :: false-or ( <base1> ) = #f

>   define  method make  ( class == <base1> , #rest  init-key-pairs , #key)
>     => ( result  :: <base1> ) ;
>   if ( authorized-make? ( init-key-pairs) )
>      next-method();
>   else
>       error ("Attempt to call make on a singleton class");
>   end if;

>  // don't allow anyone interfere later.
>  define sealed domain   make ( class == <base1> ) ;
>  define sealed domain   initialize ( class == <base1> ) ;

>  define method  get-sole-object ( class == <base1> )
>      => ( result :: <base1>);
>    *<base1>-sole-object*
>    | begin
>       *<base1>-sole-object* :=  make ( <base1> ,
>                for-internal-singleton-use-only: *singleton-init-passwd* );
>      end;
>  end method get-sole-object;  

>With the above  definitions:

>   b1 :=  get-sole-object ( <base1> ) ;   // works.  

>   b2 :=  make ( <base1> )  ;             // runtime error.

>   b3 :=  make ( <base1> , slot1: 23 ) ;  //  runtime error.

>   b4 :=  make ( <base1> , for-internal-singleton-use-only:
>"avert-thine-eyes");

> This last one works.  However, it is hard to believe that writer of that
> line doesn't know that they are purposely using a "back door".  If a
> macro is the primary user of the "back door" then the users need not
> be directly involved.

> Can a programmer who believes they have the "superior intellect" by-pass
> the

...

read more »



Fri, 21 Sep 2001 03:00:00 GMT  
 Another Singleton Pattern...


    Date: Sun, 04 Apr 1999 23:31:53 -0700

A few comments.

    I guess I'll have to take the minority position and state  that I'm
    a bit hesitant to use make as the generic to return the singleton object.

    1. The DRM allows make to return a previously allocated instance.
       I read that as allowing allocation from a pool ( if doable and
       reflective of the results that the normal protocol would provide).
       If make is suppose to be allowed to create aliases then the DRM
      (version 2) should state so explicitly.   In short  factory refurbished
      objects are fine....  it is something different to say that everyone
      has to share the same object.

I don't agree with your reading.  'make' is allowed to 'intern'.
For example, 'make(<vector>)' returns #[] -- an interned instance
of <simple-object-vector>.

      [ There is a difference between allowing the runtime environment
    implementors
        to use pooled allocation and allowing pooled allocation by user defined
        methods added to make.  In the latter case, an argument can be made
        that another generic function should be used since it must be paired
        with some sort of "free".  Different generics for different protocols. ]

      I think the expectation in a language with reference based semantics
      is that "make" creates a new object.

        foo := make ( <foo-class> )
        bar := make ( <foo-class> )

        foo.slot1 := 23 ;

I think that, to first approximation, any API that interns mutable
objects is broken.  Your example is one reason I think this.

DUIM interns all sorts of immutable objects -- regions, transforms,
colors, pens, brushes, etc.  Doing this allows for all kinds of space
and speed optimizations.

      The expectation is that  foo changes;  not foo and perhaps bar.  If I can't
      trust make not to create aliases I'd have to do explicit
      alias detection.  Shades of  C and testing the results of malloc. Sometimes
      it gives you something useful and sometimes it doesn't.

      The argument of "it is a singleton class, it is suppose to do that" works
      equally well for "don't use make because it is a singleton class".  I
    suspect
      the reason that make is used as the constructor method is because the
      "clueless" might just use it by mistake.   If they are using it
      by mistake how likely are they going to treat "make" as if it can
      create aliases?????

      [ If the designers do intend for "make" to produce aliases I would like
        to see some sort of predicate you can apply to a class to tell you
        that its make acted in this non intuitive fashion.

        I know this wouldn't be conclusive but at least you might take
        precautions for folks that did correctly follow this approach. ]

The "predicate" is part of the documentation of the API.  Any API
document that does not document that 'make' has interning behavior
should be fixed.

I didn't check the DUIM doc, but I will.

    2. On a similar a similar note:

         foo :=  make ( <foo-class>  ,  slot1: 23   ,  slot2: 53 )

         bar :=  make ( <foo-class>  ,  slot1: 1111 ,   slot2: 22222)

       After the above, I think the reasonable person would expect both of the
       following to return "true".

            foo.slot1 == 23 ;

            bar.slot1 == 1111;

       However supposedly only one of these is suppose to be the case. Which one?
       It depends.  There is no "correct" answer.   The DRM clearly states that
       these init-args are suppose to utilized by make to set the object slots
       to the specified values.  That would make the second override the first.
       The counter argument is that the first allocates but the second doesn't.
       That  is easy to see above.  Separate them into different modules in
       different libraries and would that be equally as obvious?

Any API that interns two different values of <foo-class> objects as
the same object is broken.

The fact is -- like any other language -- Dylan is powerful enough to
allow you to implement completely broken code.  It's not the place of
the language itself to legislate against such abuse.



Fri, 21 Sep 2001 03:00:00 GMT  
 Another Singleton Pattern...

Quote:

> I shall add my voice to the chorus of those who feel that returning
> singleton objects is a legitimate use of make.  If you want to be

Most certainly so. The DRM states:

"make is not required to return a newly allocated instance. It may
return a previously created instance if that is appropriate. If a new
instance is allocated, make will call initialize on the instance
before returning it."

http://www.harlequin.com/products/ads/dylan/doc/drm/drm_43.htm#MARKER...

Returning a previously allocated instance is for sure appropriate in
the case of a singleton.

Andreas

--
"It is by caffeine alone I set my mind in motion.
It is by the Beans of Java that thoughts acquire speed,
the hands acquire shaking, the shaking becomes a warning.
It is by caffeine alone I set my mind in motion." -- National Lampoon's "Doon"



Fri, 21 Sep 2001 03:00:00 GMT  
 Another Singleton Pattern...
...

Quote:
> singleton objects is a legitimate use of make.  If you want to be
> secure in the knowledge that no other programmers will circumvent
> you mechanism for returning the distinguished object, then make is
> the place for this functionality.

  If you want to be secure you would add an runtime program verifier,
  ala the JVM, to check that nothing was being done behind you back... and
  likely drop the C-FFI. ;-)

  Or have you forgotten about "sorted-applicable-methods"?  I can skip
  any "make" you may wish to put up front if I really want to.

  For the truly motivated there is little you can do to keep them from
  circumventing any mechanism.  It is a matter of degrees.

  Personally, I'm more motivated by presenting an interface that is
  useful and easy to understand (i.e. doesn't exhibit "surprising"  
  behaviour).  That way people are inclined to use it, rather than
  spending most of their time pondering how to circumvent it or
  perplexed by some non intuitive "bug".

  Using make for retrieving the instance of a singleton seems to
  be an "the ends justify the means" sort of argument, IMHO.
  It seems to me that 99% of the time you're not asking make to "make"
anything.
  You're trying to access an object.  To me that is a misnomer.

--

Lyman



Fri, 21 Sep 2001 03:00:00 GMT  
 Another Singleton Pattern...

...

Quote:
>     1. The DRM allows make to return a previously allocated instance.
>        I read that as allowing allocation from a pool ( if doable and
>        reflective of the results that the normal protocol would provide).
...
> I don't agree with your reading.  'make' is allowed to 'intern'.
> For example, 'make(<vector>)' returns #[] -- an interned instance
> of <simple-object-vector>.

  I don't see that conflicts with what I had in mind.
  Objects like  the empty list, vector,  characters, integers, etc.
  are all immutable and "special cases".  I can freely skip the
initialization
  of these objects since there not really alot to do here.  There doesn't
  have to be more then one of these since there are immutable.

  What I see is an argument to make this "special case exception" generally
  applicable.  Loopholes will always be present in standards.  When folks
  start to turn those into 10 lane super freeways is where I get off that
road.

  For example I don't really need to use  make(<vector>) to get  #[].  I
  could just use the literal itself.  Make doesn't exist for integers and
  characters, etc.   If I wanted to be sure of the type of vector then
  yes.

Quote:
> I think that, to first approximation, any API that interns mutable
> objects is broken.  Your example is one reason I think this.

  Yeah but the singleton pattern doesn't mention diddly about mutability.

  If this is significant then it should be included in the pattern
description.
  Or added as a guideline to pattern applicability.

  To some extent, I don't think it mentioned in the GoF related material
  because that pattern does NOT use the constructor to implement the
retrieval
  of the singleton.  Therefore you don't have these ambiguous interface to
the
  retreival method.  It takes one object and gives back one.  That is
  not the general interface make provides.
  [ C++ has the problem of mixed value and reference semantics so you
    can't allow the constructor to appear because there are implicit
    constructor calls all over the place. ]

  I would be more aminable to a Singleton Immutable ("at least externally")
  Object Pattern that used "make" as a retreiver.   However, I think
  the pattern is more generally applicable than that and the more general
  interface is more widely appropriate.

Quote:
> The fact is -- like any other language -- Dylan is powerful enough to
> allow you to implement completely broken code.  It's not the place of
> the language itself to legislate against such abuse.

   Sure the general building blocks can allow one to shot yourself in the
   foot.

   I put patterns into a different category. Patterns and their associated
   interfaces do not have to condone abuse.

   As for make I think in general "make" methods should exhibit approximately
   the same behaviour that default make does.  One can add an method to
   make that adds two numbers.  However, I don't think that would be
   in keeping with the spirit of what make is suppose to be doing.

---

Lyman



Fri, 21 Sep 2001 03:00:00 GMT  
 Another Singleton Pattern...

Quote:

> I shall add my voice to the chorus of those who feel that returning
> singleton objects is a legitimate use of make.  If you want to be
> secure in the knowledge that no other programmers will circumvent
> you mechanism for returning the distinguished object, then make is
> the place for this functionality.  It is the sole gateway at which
> will stand your angel with the flaming sword to prevent undistinguished
> simulacra of the designated, blessed singleton object from escaping
> and infecting your unsuspecting application.

You're reading too much Baudrillard. Take a bottle of french red wine as
an antidote. :-)

Michael

--
Michael Schuerig

http://www.schuerig.de/michael/



Fri, 21 Sep 2001 03:00:00 GMT  
 Another Singleton Pattern...

Quote:

> 'make' is allowed to 'intern'.
> For example, 'make(<vector>)' returns #[] -- an interned instance
> of <simple-object-vector>.
> DUIM interns all sorts of immutable objects -- regions, transforms,
> colors, pens, brushes, etc.  Doing this allows for all kinds of space
> and speed optimizations.

Spot the pattern: How about Flyweight?

I think it qualifies. Immutable state is kept in shared objects, the
specifics are externalized (are they in DUIM?).

Michael

--
Michael Schuerig

http://www.schuerig.de/michael/



Fri, 21 Sep 2001 03:00:00 GMT  
 Another Singleton Pattern...


    Date: Mon, 5 Apr 1999 20:21:12 +0200


    > 'make' is allowed to 'intern'.
    > For example, 'make(<vector>)' returns #[] -- an interned instance
    > of <simple-object-vector>.

    > DUIM interns all sorts of immutable objects -- regions, transforms,
    > colors, pens, brushes, etc.  Doing this allows for all kinds of space
    > and speed optimizations.

    Spot the pattern: How about Flyweight?

Nope.

    I think it qualifies. Immutable state is kept in shared objects, the
    specifics are externalized (are they in DUIM?).

These objects have no "externalized" state and no mutable state.  They
_are_ the "immutable state ... kept in shared objects".



Fri, 21 Sep 2001 03:00:00 GMT  
 Another Singleton Pattern...

Quote:

> I guess I'll have to take the minority position and state  that I'm
> a bit hesitant to use make as the generic to return the singleton
> object.

I don't think there are hard and fast rules, it's a matter of taste
and judgement, though clear code is generally a good thing!

It is somewhat unintuitive initially but, in Dylan, 'make' really means
'obtain-instance', and how that instance is obtained is up to the
implementation.  If there's anything unusual which clients need to
understand, document it; if they don't read the doc, tough!  As it
says in the book "Writing Solid Code" (MS Press), "Don't guess the API."

In general, unless there's some good reason not to, it's usually
helpful to allow use of 'make' to instantiate singleton classes, so
clients can write generic code parameterised on a class to create.
You might also have some '$canonical-instance', and advertise that
as the preferred access point.

In addition, Consequence 4 of this pattern in the GoF book is that
you can change the implementation to allow a fixed or open larger
number of instances.  If you have a design where that might be
appropriate, using 'make' in Dylan would seem more natural, to me.

[I take your point about #[] not being a good example of the Singleton
pattern, BTW.  A better example is #f and #t; and the only reason (I
think) for not allowing <boolean> to be instantiable is that any
expression for creating an instance would have to refer to one of the
canonical instances, therefore being pointlessly longwinded.  OTOH,
you might argue that "make(<boolean>, ...)" could take one keyword
whose true/false value determines the #t/#f result.)

Quote:
> 1. The DRM allows make to return a previously allocated instance.
>    I read that as allowing allocation from a pool ...
>    If make is suppose to be allowed to create aliases then the DRM
>   (version 2) should state so explicitly.   ...

The first paragraph of the description for 'make' says:

  Returns [a general] instance of /type/, with characteristics
  specified by keyword arguments.

That's all you can really expect of make, unless the class/type doc
says otherwise.

Quote:
>   [ ... In the [case of user-defined pooled allocation],
>     an argument can be made that another generic function
>     should be used since it must be paired with some sort of "free".
>     Different generics for different protocols. ]

Sometimes you could do this by relying on weak tables and garbage
collection, without needing to extend the protocol.  But if there is
a need for an explicit "release" operation then yes, personally, I'd be
inclined to use a different "obtain" function from "make" (even if it
just called make).

Quote:
>   I think the expectation in a language with reference based semantics
>   is that "make" creates a new object.

Ah, yes, that's the catch: that may or may not be a reasonable
expectation, but it's wrong in Dylan!

Quote:
>   ...  I suspect the reason that make is used as the constructor
>   method is because the "clueless" might just use it by mistake.
>   If they are using it by mistake how likely are they going to treat
>   "make" as if it can create aliases?????

I think the reason is that it becomes a natural idiom, and starts to
make sense :-)  The clueless, as I said, should read the doc!

Quote:
>   [ If the designers do intend for "make" to produce aliases I would
>     like to see some sort of predicate you can apply to a class to
>     tell you that its make acted in this non intuitive fashion.

Not possible automatically, since make can perform arbitrary
computation.
You could manually write methods on some predicate, but I can't see how
you would use it in practice.  Can you give some non-trivial example
code snippet?  (I can believe there are possibilities I'm not seeing!)

Quote:
> 2. On a similar a similar note:

>      foo :=  make ( <foo-class>  ,  slot1: 23   ,  slot2: 53 )
>      bar :=  make ( <foo-class>  ,  slot1: 1111 ,   slot2: 22222)

>    After the above, I think the reasonable person would expect both
>    of the following to return "true".

>         foo.slot1 == 23 ;
>         bar.slot1 == 1111;

>    However supposedly only one of these is suppose to be the case.
>    Which one? ... There is no "correct" answer.   The DRM ... states
>    that these init-args are suppose to utilized by make to set the
>    object slots to the specified values.  That would make the second
>    override the first.  The counter argument is that the first
>    allocates but the second doesn't.  That  is easy to see above.

>    Separate them into different modules in different libraries and
>    would that be equally as obvious?

If you have a singleton class being instantiated in more than one place,
you should either co-ordinate it centrally, so participants can know
what's happened, or prevent it altogether.

Quote:
>    For the suggested implementations, the first is true and the second
>    is false.  ... Again this seems to be a violation of the
>    instantiation/initialization protocol put forward in the DRM. ...

Yes, as the instance should match the keyword characteristics, newly
allocated or not.  If the make method can't or won't do this, perhaps
it should signal an exception.

I think this is the key: make doesn't just return an instance, it
also initialises it, which isn't part of the Singleton pattern.  So
another way to implement Singleton with 'make' would be to have a 'make'
method which accepts no keyword arguments.  Less convenient but truer
to the pattern.

OTOOH, sometimes you might conceptually have a "volatile" singleton,
where you don't necessarily expect the state to last, it's just a
suggested starting point, expected to be changed behind your back by
outside forces.  Again, it's a matter of judgement.

Quote:
>    [ I think the presented solution would be better if it disallowed
>      the specification of initialization arguments.   The semantics of
>      what "should" happen are ambiguous.  However, I'm not adverse
>      to make invoking an error. ]

Oh, it seems you already thought of all that!

Quote:
> 3. [In] the Smalltalk Companion book [, they] suggest that there is
>    usage of the Singleton pattern that isn't persistent.  ...

>    The solution discussed so far would make it difficult to transition
>    from one instance to another.  Exactly how to you create a new
>    instance now that you have taken away make?

I'm not sure how to answer this, without knowing how/when the change
is expected to happen.  Does the "old" one just get GCd, or what?

Quote:
>    [ The upside of deviating dramatically from the DRM initialization
>      "standard modus operandi" for make is that no matter how misused;
>      make will never return an error.  ...  ]

Um, I missed your point here.

Quote:
> The alternative I've been thinking of for a while would utilize the
> initialization key value pairs to "authorize" make being invoked by
> code "private" to the singleton class while invoking a runtime error if
> used "improperly".

Ick!  It works but it's ugleee!  If you really want this much security,
how about a module-private thread variable, like this:

--------
// not exported
define thread variable *internal-make?* :: <boolean> = #f;

define method make ...
  *my-instance*
    | if (*internal-make?*) *my-instance* := next-method(...)
      else error(...) end
end

define method obtain-sole-instance ()
  dynamic-bind (*internal-make?* = #t)
    make(<my-class>, ...)
  end
end
--------



Sat, 22 Sep 2001 03:00:00 GMT  
 Another Singleton Pattern...
...

Quote:
> Most certainly so. The DRM states:

> "make is not required to return a newly allocated instance. It may
> return a previously created instance if that is appropriate. If a new
> instance is allocated, make will call initialize on the instance
> before returning it."

  This is open to interpretation. Namely  "not required" and "if that is
  appropriate" mean exactly what?   What is appropriate?

  make ( <list> ) ==>   #()

  (  make ( <list> )  =   make ( <list> )  ) ==>  #t

  Sure there is one relatively stateless and definitely immutable sentinel
  that is shared by the end of all lists.

  However,

  make ( <very-stateful-very-mutable> )  =  make (
<very-stateful-very-mutable>)

  I don't particularly think so.

  I think there is a difference between "there is only one way to
  represent the end of a list  or the empty string or perhaps the empty
vector"
  and there is only one instance, of many possible instances, of a class.

  The GoF lists the intent of the Singleton as

  "Ensure a class only has one instance, and provide a global point of
   access to it."

  Essentially, this can be accomplished by a "virtual slot" on the class.
  While make will certainly be involved in the "ensure" part of that
  description.  I see make as a constructor/creator not as an
  accessor/retriever. The name "make" seems like a very funny name for
  a "virtual slot".  Therefore, it need not be "visible" in implementing
  the latter half.

 / \
  | statefullness
  |
  |
  |
  |
  |
  |
  |
  |
  ------------------------------> mutability

  I think the intended notion of "appropriate" is being directly on that
  vertical axis. Personally I think it should be more toward the origin as
  well. [ Also covered are perhaps some extremely special cases off of it.]
The
  farther you drift out into the quadrant, the more dubious using make
becomes
  as the correct "name" to attach this access method to, IMHO.

  Notice that skipping the initialization method in this context doesn't
  harm much is there is not all that much to initialize.

  In short, "appropriate" is a somewhat unspecified restricted subset
  of situations.  It doesn't mean "whenever the method writer feels
  like it".

--

Lyman



Sat, 22 Sep 2001 03:00:00 GMT  
 Another Singleton Pattern...
...

Quote:

...
> I don't think there are hard and fast rules, it's a matter of taste
> and judgement, though clear code is generally a good thing!

  Which is one reason I entitled the post "Another" singleton pattern.

  Even if no one buys into the alternative I presented I was/am concerned
  with the implicit assumptions that seemed to be present in the
  "use make" approach.

Quote:
> It is somewhat unintuitive initially but, in Dylan, 'make' really means
> 'obtain-instance', and how that instance is obtained is up to the
> implementation.

  Well if that explanation where in the DRM it would outline the
  notion of "if appropriate".  :-)

Quote:
> helpful to allow use of 'make' to instantiate singleton classes, so
> clients can write generic code parameterised on a class to create.

  If clients want parameterized code you can always pass along the
  "constructor" generic function.  I see no need to always use the
  same name.   There is the convenience of one less argument in
  parameterization, though.  

Quote:
> In addition, Consequence 4 of this pattern in the GoF book is that
> you can change the implementation to allow a fixed or open larger
> number of instances.  If you have a design where that might be
> appropriate, using 'make' in Dylan would seem more natural, to me.

  While "get-sole-instance" would be a misnomer. I'm still hestitant
  about "make" being an general alias generator.

Quote:
> >   I think the expectation in a language with reference based semantics
> >   is that "make" creates a new object.

> Ah, yes, that's the catch: that may or may not be a reasonable
> expectation, but it's wrong in Dylan!

  I think there is a difference between a small set of exceptions
  ( the empty list, the empty string, etc.) and encouraging a
  cornucopia of expectation violations.  This is a probably a
  matter personal judgement though.

Quote:
> You could manually write methods on some predicate, but I can't see how
> you would use it in practice.  Can you give some non-trivial example
> code snippet?  (I can believe there are possibilities I'm not seeing!)

    new-var := make ( <foo> , .... ) ;
    if  (  does-make-create-aliases? ( <foo> ) and
          ~ different-from-the-others? ( new-var , others ) )
            ... do whatever ...
        end;
    end if;

  This could be created by the impacted user by reading the docs though.

Quote:
> >         foo.slot1 == 23 ;
> >         bar.slot1 == 1111;

....
> >    Separate them into different modules in different libraries and
> >    would that be equally as obvious?

> If you have a singleton class being instantiated in more than one place,
> you should either co-ordinate it centrally, so participants can know
> what's happened, or prevent it altogether.

   Who should do the coordinating?   The object or the programmer?
   Brief Scenario:
                 Programmer A writes a library with a singleton class.
                 Programmer B writes a library that uses this singleton class
                                      and some code written by programmer C
                 Programmer C write some code that happens to make an
                               intermediary copy to side-effect in the
                               process of doing a computation.

   Programmer B has to know about A's class is singleton class. Fine, read
   the docs.  However, he also must know that C will want to make an
   intermediary copy.  That seems more than B should have to know.

   As long as the singleton is an immutable object then there is no
   reason for  C to copy and side-effect. C might think he has
   made a copy, but it doesn't really have significant ramifications.
   [ Well perhaps as an index into a table... ].

Quote:
>> ... changing singletons...

> I'm not sure how to answer this, without knowing how/when the change
> is expected to happen.  Does the "old" one just get GCd, or what?

  Persumably it gets GCd away.
  I don't recall the Smalltalk Patterns book's example but the following
  somewhat contrieved example might do.

  There is a "recorder" object that all actions are reported to.
  [ Something akin to "dribble" in Lisp or  "script" at the shell prompt. ]

  Now there should only ever be one of these. The intially this could be
  set to the "redirect to /dev/null" recorder.  Later there
  could be a  switch to something that has to record into a more presistant
  location.  

  The singleton's role to provide global access to the recorder. It need
  not be the same one all the time.

Quote:
> >    [ The upside of deviating dramatically from the DRM initialization
> >      "standard modus operandi" for make is that no matter how misused;
> >      make will never return an error.  ...  ]

> Um, I missed your point here.

  I shouldn't have said "never return error".  This was partially related
  to the  keyword initialization having no effect.  As you pointed out
  it should "reset" to whatever prescribed.  The "upside" to always
  attempting to do the right thing instead of signalling a error
  means you don't have to worry about make complaining about
  it cannot "obtain" the object.

Quote:
> Ick!  It works but it's ugleee!  If you really want this much security,
> how about a module-private thread variable, like this:

  dynamic scoping to the rescue. ;-)  No, dynamic scoping didn't
  immediately leap into my mind.   That might be useful independent of
  being in the thread library.  :-)

  It wasn't so much the "security" that I was after, but the programmer
  being fully aware that they were taking a "back door".  Making it
  ugly is a disincentive to it being used explicitly ( as opposed to
  within a macro). :-)

--

Lyman



Sat, 22 Sep 2001 03:00:00 GMT  
 Another Singleton Pattern...


Quote:

>...
>> Most certainly so. The DRM states:

>> "make is not required to return a newly allocated instance. It may
>> return a previously created instance if that is appropriate. If a new
>> instance is allocated, make will call initialize on the instance
>> before returning it."

>  This is open to interpretation. Namely  "not required" and "if that is
>  appropriate" mean exactly what?   What is appropriate?

Appropriate is absolutely whatever the designer of the API thinks is
appropriate.  The only rule is that the value returned be a general
instance of the first argument to make.

You can use this to implement the singleton pattern, to implement
interning (for example, interning strings and returning symbols),
and to implement any of a number of other things.  You can have a
class with five mutable instance, and every time you call make
you get back one of these five instances chosen at random, with
the instance's internal counter incremented before it is returned.
Absolutely whatever you want.

You can argue that it's not a good idea to take advantage of this
flexibility (and indeed, you appear to have been arguing that).
But the intent of the DRM should be a non-issue.



Sat, 22 Sep 2001 03:00:00 GMT  
 Another Singleton Pattern...
I think it is necessary to distinguish two issues: (1) the nature of make() and
(2) the nature of the creational patterns at hand (Singleton, Flyweight, ...),
because the way people intermix the two in my eyes doesn't make much sense. And
some simply misunderstood the patterns they cite.

Whether or not make() may return the same instance is a matter of personal
taste. The DRM says it can do so, so why not? This is more of a *language*
issue.

The other issue is how to treat creation and management of objects that share
some commonality besides variability, which is mostly about *design*.
Contributers cited something like this as their example:

     I make(<foo>, bar: baz) and make(<foo>, bar: zang). What should I do?
     Return the same instance? Is it a Singleton or a Flyweight?

Singleton is about how to have exactly one object per class. This is about
identity, not about state. If I define a class to be a Singleton, there is by
definition only one instance of this class. Nothing is said about what value
its slots carry. People discussed things about #[] and #() and whatever else.
Are these Singletons? Not so if they are instances of <vector> and <list>,
respectively, because there are many other instances of <vector>, e.g. #[1, 2,
3]. However, they are (can be) Singletons if they are (the only) instances of
<empty-vector> and <empty-list>. Early versions of Mindy had <true> and <false>
in their class graph, for example.

Flyweight is about sharing to support large numbers of objects. The
applicability section (GoF, p. 197) clearly states to use the "pattern [only]
when all of the following are true":

   * Most object state can be made extrinsic.
   * etc.

Now what about this? For #[] and #() there is no intrinsic state that can vary,
they are immutable. You can make (call) them Flyweights if you please. In that
case make() would serve as the factory. I don't feel this would be good, but be
it as it is. But calling #[1, 2, 3] a Flyweight? Certainly no, because almost
all state (contents, length, ...) is intrinsic, it can't be made extrinsic.
There is nothing to share but the reference to the object-class(), which is
definitely not worthy to apply the pattern.

As an aside, there's a pattern called Value Object, which is related to this
subject. Visit Ubilab's Web site on:

     http://www.ubs.com/e/index/about/ubilab/print_versions/pdf/bae98.pdf

HW
--
Phone: +41-1-2348754
Fax: +41-1-2364671

Web: http://www.ubs.com/ubilab



Sun, 23 Sep 2001 03:00:00 GMT  
 
 [ 14 post ] 

 Relevant Pages 

1. Implementation of the Singleton-Pattern

2. Singleton Pattern in Eiffel

3. singleton pattern

4. A simpler singleton pattern?

5. Singleton Pattern and Python

6. Extending Python with C++ singleton pattern using boost python lib

7. Extending Python with C++ singleton pattern using boost pytho n lib

8. Python and the Singleton Pattern

9. Singleton pattern ?

10. Singleton Pattern

11. Singleton Pattern and Teaching Python

12. XOTcl : Singleton pattern not working

 

 
Powered by phpBB® Forum Software