Rework static scope access

One of the things that has been confusing with the introduction of static members is the difference between marking a member as static at “compile-time” in the static constructor (public static a = 1) and accessing the static scope at “runtime” in the instance (static.a = 1). This can be very confusing, especially to the uninitiated.

My proposal then is to rework the static scope as follows. The current “static” scope is renamed to “self” and is bound to the declaring class. Second, a static scope is added that provides “late static binding”. Both scopes should use the scope-resolution operator to access/update members.

// A.lucee
class {
    static {
        private a = 1;
        private b = 2;
        private c = 10;

        public function getSelfA() {
            // these should error
            return this.a; // "this" only available to instances
            return static.a; // invalid use of static scope - requires double-colon
            return self.a; // invalid use of self scope - requires double-colon

            // these should work
            return a;
            return self::a;
        }

        public function getStaticB() {
            // late-static binding static scope introduced.
            return static::b;
        }
    }

    // access static scope from implicit instance constructor...
    self::a = 7;
}

// B.lucee
class extends A{
    static {
        private a = 3;
        private b = 4;

        public function getC() {
            return self::c;  // error - no member c found
            return static::c; // error - no member c found
        }
    }
}

// index.lucee
echo(A::getSelfA()); // 1
echo(B::getSelfA()); // 1

echo(A::getStaticB()); // 2
echo(B::getStaticB()); // 4

new A();

echo(A::getStaticA()); // 7

I’m not disagreeing with your suggestion, but I think it is begging the question slightly at the moment.

Can you explain what it is that is confusing about the status quo, and how your suggestion is an improvement. I think you’re starting your post on an unstated / unexplained assumption that is in your head but never made its way into your post.

Cheers.

2 Likes

I’m with Adam here: I don’t see anything confusing about the status quo in the context of CFML.

I can see how it’s not like Java, but CFML != Java (and that’s a good thing IMO).

I think your distinction of “late-binding static” is very confusing so that seems worse to me. CFML doesn’t have layered scopes that hide inherited scopes so your proposal seems completely at odds with how this and variables work.

@adam_cameron, you are correct. There are a few things. My starting point is from an understanding of how static members work in PHP, which you may have picked up.

That in mind, and seeing as they chose the double colon for static member access externally, my first thought was that it would be nice to have a measure of consistency when it comes to accessing static members internally, thus going with PHPs self:: syntax. Contrast that to Lucee where there are 3 different ways to access statics internally, depending on if you are in the static block:

class {
    static {
        private n=0;
        public nextN() {
            // no scope
            return n++;
            // "this" scope :/ only tried it here
            return this.n++;
            // static scope
            return static.n++;
        }
    }
}

Not very consistent IMO.

Also, I did fail to reference one issue on the tracker which also may have given more context. I don’t remember the issue number, but there is an open issue to make this possible:

class {
    public static a = 1;
}

That is great - I like that better than the static block syntax. Not that I hate the static block, I just like this one better. BTW, that syntax already works for static member methods.

Couple that with the fact that this too is already working right now:

class {
    public static.a = 1;  // public is required here BTW, otherwise it defaults to private...
}

Do you see the difference? The first example would set the static member at class load, whereas the second sets it at class instantiation (that is, static a doesn’t exist in the second example until you first create an instance of the class). I have to admit this behavior caught me off guard for a second until I thought about how and where I was accessing the static scope.

@seancorfield, as far as the late static binding, it can be left out. That would be additional new behavior. It was a feature that was sought and anticipated for PHP, and one I was familiar with. But I suppose it isn’t necessary here.

Statics has been my most anticipated feature of all for Lucee. I have experienced what I see are areas prone to confusion. I feel my proposal provides a more simple, clear, and consistent means of accessing static members internally at runtime.

Ok, it seems that something along these lines was already in the works. I had been working with 5.0.0.65-BETA to do my testing. I found out how to pull the latest snapshot (5.0.0.86), and it no longer supports using the “this” or “static” scopes internally. Instead you apparently must always use [className]::staticMember to access static members. That is satisfactory to me. It would be nice to add self:: as a reference to the current class, for those situations where you have long class names. Example: IncrediblyAwesomeWidgetFactory::CONSTANT_DATA instead is self::CONSTANT_DATA.

There are some wiki pages/blog posts that will need to be updated.

Agreed. Ticket to vote on?

https://luceeserver.atlassian.net/browse/LDEV-560

please see my comments in the ticket:

static scopes was not removed as you can also see in the testcases for static:

that get sucessfull executed with every build process

i personally don’t like “self” because it is not clear that this would be about the “class reference” of the component and not the instance, in other words self could be understand as alias for “this”.