Skip to content
December 9, 2008 / Abe Pralle

Automatic singletons and eliminating class members

It’s the same story as always.  After being mildly irritated by something for a long period of time, I finally notice it and then decide to do something about it.

This time it’s class properties and methods – also known as static variables and methods.  Not only do they complicate my compilers (“do everything twice… once for the class members and once for the object members”) and not only are they hard for beginners to grasp, but they’re just plain awkward to use.  Inheritance doesn’t work quite right (you can’t use super.method_name()), you can’t give anything a “this” reference, and you can’t get Class object metadata or do a toString().

Thinking about it today, I realized something important and fundamental: class members are a kludge.

Currently, Slag and Java class members are essentially global variables within separate namespaces.  That makes sense given the whole history of C → C++ → Java → Slag.  But really, what are they used for?  Two things: managing objects of a class (class-wide settings and defaults, factory methods) and implementing singletons (where you only ever have one object of a class that’s usually accessible from anywhere).

In fact, I assert that both things can be accomplished via singleton objects, with the “defaults and settings” lifted out of the class and made into a separate “manager”.  Singletons are a tad klunky, but not too bad (see also: understanding class members and singletons).  But how about we make them even simpler to create and use?

I hereby present the singleton object in gen2 Slag.  Here’s some mock-up gen1 Slag code that uses class members:

  class Image
    CLASS_PROPERTIES
      default_handle=HANDLE.center : Vector2
    CLASS_METHODS
      method create(...):  return Image(...,default_handle,...)
  endClass
  ...
  return Image.create(...)

And here’s some gen2 code that uses a singleton object:

  singleton ImageFactory
    PROPERTIES
      default_handle=HANDLE.center : Vector2
    METHODS
      method create(...):  return Image(...,default_handle,...)
  endSingleton
  ...
  return ImageFactory.create(...)

Underneath, a singleton is really an object of the singleton class that’s created at the beginning of runtime (now instead of class member initialization) – a singleton object is a regular object of a regular class.  Not only does this solve the inconsistencies that come with class members, it also greatly simplifies the compiler!

16 Comments

Leave a Comment
  1. Eric Hoefler / Dec 9 2008 6:28 am

    Hey Abe … just wanted to let you know that I’m a reader/fan! I subscribe to your site, but I rarely understand what the hell you’re talking about. Still, you sound really, really smart, so keep it up!

  2. Jacob Stevens / Dec 9 2008 9:31 am

    I understand (and agree with) your point, but I have to admit that this change is a little bit scary! No global properties and methods, eek!

    I assume that a singleton can also be instantiated as a non-singleton object? There are cases when I use a singleton *and* normal objects of the same class. If you think this will be possible, I’d suggest:

    singleton class ImageFactory
    PROPERTIES
    default_handle=HANDLE.center : Vector2
    METHODS
    method create(…): return Image(…,default_handle,…)
    endClass

    The singleton keyword would simply be a directive to make a global object of type ImageFactory called ImageFactory.

    I’m going to have to mull this over to decide if I have any major objections… a brave new world!

  3. abepralle / Dec 9 2008 9:37 am

    Thanks Eric. 🙂 Like most technical things, the concepts aren’t really all that bad, it’s just a large body of knowledge and a small dictionary of lingo to become familiar with.

    I’m actually on the fence a lot of times over how much background to give. I do like giving more details because as an (originally) self-taught programmer, all the background and lingo was a big intimidating wall. I just haven’t been sure that anyone would be reading who could use the extra info, but now I’ll definitely start putting in a little more background!

  4. abepralle / Dec 9 2008 9:40 am

    On your point, Jacob: I’m not really *against* being able to do that – but do you have any situations in mind where you’d actually want to?

  5. Jacob Stevens / Dec 9 2008 9:58 am

    Here’s an example:

    Disclaimer: I don’t claim to be the best OO designer in the world, but the following example did work, and seemed rather clean at the time.

    Awhile ago we were prototyping a puzzle game that required a level editor. I implemented the state of the world as a large singleton called WorldState. To make modifications, I’d call methods like WorldState.getInstance()

    I also wanted to implement an undo stack. Since the entire state of the world was held in a WorldState object, I just implmented a deep_copy() method so that I could save off other WorldState objects in the undo list. Then, when you undo, I just deep copy from the undo list back into the singleton instance.

    In general, I think there are arguments for using this in cases where you can a “main” version of an object, but also reasons for storing non-main versions.

    Again, I know there are other ways to do this, but it seems like a sensible solution in some cases.

  6. abepralle / Dec 9 2008 10:43 am

    Great example Jacob! So, sure: if GameState is a singleton class, then GameState refers to the singleton object and GameState() makes a new instance of the class.

    And – all right, I guess “singleton class X” is a little nicer than just “singleton X”.

  7. Noah Hilt / Dec 9 2008 11:22 am

    I’ve always wanted a language that implemented the singleton pattern just like this, awesome!

  8. Paul Stevens / Dec 9 2008 3:09 pm

    Here’s my first concern: I use class properties EVERYWHERE as a way of defining class-wide settings and defaults. Some examples: state constants, position constants, speed constants, gravity, image constants, flag constants, etc. etc. etc. For the usual reasons, instance variables or augments to Global just won’t do.

    Does this mean I would need to write a singleton for nearly every regular class I write?

    Additionally, would I always need to access class properties using SomeSingleton.property, rather than just referring to the property directly? In many cases, only one class or a small class hierarchy should have access to a group of class members. Does it make sense to demand that these classes reference their own exclusive members indirectly, by asking for the values from another object?

    A less theoretical concern is that since I use class properties all over the place, that’s a lot of typing Singleton.property, and having to remember the corresponding singleton’s name for each of my classes.

    Great discussion!

  9. abepralle / Dec 9 2008 5:34 pm

    On the one hand I would argue that most constants like that *should* be separated from their object definitions for purposes of clarity and separation of concerns. It never felt quite right to me to lump a bunch of things that aren’t created per-object with other things that are, and this new idea feels *so* right to me. If I have some image filenames defined as constants, I’d rather have those in one ImageFilenames singleton than scattered across several Entity classes or whatever.

    On the other hand, I don’t want to force people to abandon a style that they like, and probably most first-time Slaggers are gonna be coming from Java. And so – here’s a cool part – since class members are a sort of kludgy way to do singletons (IMO), then by having singletons we can still have class members – they’ll just use a different underlying mechanism!

    So here’s my new plan: I’ll implement singletons as I’ve described. Then I’ll add a new member category “IMPLICIT_CONTEXT” under which you can list as many singletons as you want – if “ActorClassProperties” is listed in class Actor, then some_actor.state_idle will check both properties in Actor and then properties in ActorClassProperties (the new compiler is actually already set up to allow implicit contexts such as this, in fact).

    But then you won’t actually have to mess with any of that! You can just declare CLASS_PROPERTIES and CLASS_METHODS as before and they’ll be automatically transformed into a singleton that becomes part of the IMPLICIT_CONTEXT of a class!

  10. Paul Stevens / Dec 9 2008 6:48 pm

    Hard to argue with that! Would all classes have Global as an implicit context automatically?

  11. Abe Pralle / Dec 9 2008 6:50 pm

    Yes!

  12. joeedh / Dec 19 2008 9:07 pm

    wow this is awesome! this is why I love slag 🙂

  13. Timothy Goya / Jan 12 2009 1:47 pm

    To me, there is one more property about singletons that differentiates it from merely static data. Singletons initialize on first use, whereas statics initialize at some nondeterministic time before main().

    I can see examples of both semi-singletons in which other instances are legal but one is designated The Object, and pure singletons in which other instances are not legal. Perhaps “singleton class X” can refer to the semi-singleton and “singleton X” (without class) can refer to pure singletons.

  14. Abe Pralle / Jan 12 2009 3:00 pm

    Yeah, I agree that singletons often have that characteristic and I’ve been wondering about the best way to handle that. It would be easy enough to use a keyword as you say, but on the other hand I’m not convinced that people need a “null reference that self initializes” versus just a call to a setup method at some point. Deferring initialization suggests that the setup is more order-sensitive, but then in that case can we just leave the constructor empty and call setup() later on when we’re ready?

    I’d love to see some good use cases for deferred initialization.

  15. Timothy Goya / Jan 24 2009 9:24 pm

    I just stumbled onto the code I wrote that made me think deferred initialization was an important property of a singleton. In one of the projects I’ve worked on I created a C++ wrapper to the physfs library. This wrapper contains a singleton that creates a nice interface for managing the virtual file system and does phyfs initialization in the constructor and deinitialization in the destructor (RAII, kind of). The physfs library uses argv[0] in initialization, so the program should enter main before it initializes the singleton. I could have a separate setup method, but then the singleton is in a funny state between construction and setup that the user could entirely too easily do something invalid. If the singleton won’t actually be usable, it shouldn’t get created in the first place. I’m still not sure how to properly handle singletons that need initialization parameters. I don’t like forcing the user to do some incantation before retrieving the singleton and avoid requiring that whenever possible.

    I’m not sure if any of this applies in a more flexible dynamic language like Slag.

Trackbacks

  1. Understanding class members and singletons « Abe’s Codeblog

Leave a reply to Jacob Stevens Cancel reply