I can haz constant?

masak on 2010-04-22T13:31:57

Let's try this format. Mixing #perl6 IRC logs with more detailed explanations, sort of like a movie and a commentator track.

<masak> over lunch, since I'm so into scoping of different kinds right now, we got to talking about class-bound variables again. my colleague jonalv, whose home language is Java, thought that it was a shocking omission not to have class-based variables.
<masak> I explained how 'my' and 'our' didn't do what was intended, since lexpads and packages are orthogonal to OO inheritance.

"Orthogonal" is quickly becoming one of my favourite terms in language design. It doesn't mean "at an angle of ninety degrees", but rather "along independent dimensional axes", that is, "not having anything to do with one another".

The two things that are orthogonal here are ordinary variable scoping on the one hand, and (method) inheritance on the other. In particular, a deriving class typically isn't lexically nested inside its base class, nor is it a subpackage of it, not even in the case of A::B deriving from A. (Unless they're nested in code, but in the general case they aren't.)

<masak> when I talked about 'state' variables, he was a bit mollified and admitted that the need wasn't as great for class-bound variables.
<masak> and when I explained about the non-need for class-level methods due to type objects, I had almost convinced myself that we don't need class/static variables :)
<masak> but one use case that I can see is something akin to a constant defined in a class, which ought to be reachable from all its methods and the methods in derived classes.

When I learned about static fields in Java, even the examples were contrived. They looked much like this:

class Car { // this is Java code
    static long cars_produced = 0;

    public Car() {
        ++cars_produced;
    }
}

Here's where Perl 6's state initializer feels a little bit like Java's static scope declarator.

class Car { # this is Perl 6 code
    submethod BUILD() {
        state $cars-produced = 0;
        ++$cars-produced;
    }
}

In both pieces of code above, the variable counting all the cars ever produced since the beginning of the program will begin at 0 and increase by one every time we construct a new car.

However, the visibility is different. In Perl 6, the variable is only visible inside the BUILD submethod. If we want all methods to see it, we'll have to move it out to the larger class scope. (And then we don't need state, because the class block is only run once. We can use my to tie it to the lexical class block, or our to tie it to the class package. In the latter case, it can be referred to from the outside as Car::cars-produced.)

But that still doesn't give us the inheritance that we like to associate with classes. The Java code would keep ticking up cars even if we derived a RollsRoyce class from Car, as long as we called super() from within the RollsRoyce constructor. The Perl 6 code will behave the same (and automatically) since we put our initialization in the Car.BUILD submethod, which would get called by RollsRoyce.BUILDALL. But in Perl 6, we can only see the variable when inside Car, not when inside RollsRoyce. Java doesn't have this issue.

Excuse the crappy non-real-life example. 哈哈 But two more realistic use cases bring us back to the IRC discussion in question.

The first is one or more constants that a class might want to share with its deriving classes. That feels pretty natural. The second is enums, which are basically constants packaged in a convenient form.

I like throwing out items for discussion like this on #perl6. You never know who will pick them up, but I usually learn something from them, and sometimes the spec even gets improved as a result. This time, TimToady replied:

<TimToady> std: has constant $.pi = 3;
<p6eval> std 30419: OUTPUT«ok 00:01 110m␤»
<jnthn> o.O
<TimToady> masak++ was conjecturing class-based constants, but it already falls out
<TimToady> in fact, that was one of the reasons the constant declarator moved from being a scope_declarator to being a type-declarator, so we could use it in arbitrary scopes

Well, that does take care of the constants use case. Nice! You use has and twigils to get you the inheritance behaviour. Why didn't I think of that?

Here's why I think it's extra nice: rather than make this an issue of scoping and visibility, the has constant construct makes it an issue of immutability. Given this information, the compiler is free to optimize as much as it can, but (unlike Java) we never had any need to invent a "class level" scope, where static things are stored. That aligns with the rest of Perl 6; we don't have 'static methods' either, for example — but you can achieve much the same things through other means.

<masak> TimToady: 'has constant' still makes me happy. what's my best solution if I want to do something similar with an enum? (i.e. share it between a class and all its descendants.) enum is also a type declarator, but the name doesn't have a twigil...
[...]
<TimToady> masak: testing a patch for 'has enum $.meth <foo bar>'
<masak> TimToady: \o/

I love it when existing parts of the design just melt together into something even more useful than the sum of its constituent parts. The fact that I can be part of that process makes the work on Perl 6 feel much less like work and much more like an adventure.

So now, I can haz constant! And enums! I expect they will come in handy, especially since I will be on the lookout for possible uses for them.

As for class-based variables, Perl 6 still doesn't have them. I don't see a similarly good way to add them to the language. On the other hand, I also don't have a better use case for them than that crappy Car example.


object refcounting

gilleain on 2010-04-22T15:08:40

The only real (non car-based :) example I can remember seeing for static class variables is reference counting:

public class MyBaseObject {

    public static int REFCOUNT;

    public MyBaseObject() {
        REFCOUNT++;
    }
}

public class DerivedObject extends MyBaseObject {

    public DerivedObject() {
        super();
    }

    public static void main(String[] args) {
        for (int i = 0 ; i < 10; i++) {
            DerivedObject d = new DerivedObject();
            System.out.print(d.REFCOUNT + " ");
        }
    }
}

prints "1 2 3 4 5 6 7 8 9 10 " as you might expect.

Re:object refcounting

masak on 2010-04-22T15:18:11

In other words, my Car example, but without the Car. :)

Is it common to want to implement reference counting in this way? What's the use case?

Re:object refcounting

gilleain on 2010-04-22T15:27:54

Doh. Yes, I didn't think that through really, did I?

I have never used static class variables in Java. They seem like a subtle disaster waiting to happen. Also I try (where possible) to program without side-effects.

Moreover, Java doesn't have proper destructors, so you can't decrease the count in any reasonable way.

Is there perhaps a more reasonable use case for a kind of object-level namespace? It would be horrible in practice, but something like public static HashMap MyBaseObject.OBJECT_VARIABLES...

Sounds like a guarantee of threading problems to me, though.

How about this?

spinclad on 2010-04-27T19:46:27

class Car { # this is Perl 6 code
    my $cars-produced = 0;
    has $.cars-produced := $cars-produced;

    submethod BUILD() {
        ++$cars-produced;
    }
}

So you have a shared counter, an alias of it per instance (which seems redundant, but makes it work*... perhaps by some means one could persuade the compiler to compile it away, as it's constant as a reference), and accessors for inheriting.

Rakudo doesn't like the 'has $.x := $y' construct (yet?), nor other variations moving the bind into the BUILD, but std parses it fine, at least. So when I say 'makes it work', I mean 'in Perl 6, in theory, in my head'.

Re:How about this?

masak on 2010-04-27T19:58:13

I was reminded by TimToady that something very much like this is already possible class Car { my $.cars-produced = 0; #`[...] }. It's spec'd and everything, in S12:731. Hope that helps. :)

Re:How about this?

spinclad on 2010-04-27T21:40:08

Oh, yes, that's exactly the thing.

I think has $.x := $y would have its uses, too; i'll poke around (in spec, code, #perl6) to see what might block/enable it.

Gilad Bracha doesn't like static state

masak on 2010-05-08T09:50:23

I found this interesting post where Gilad Bracha rants about static state in objects. Makes me think Perl 6, for all the static state it hasn't, maybe has a bit too much anyway. :)

And an mjd post

masak on 2010-05-21T12:34:37

I keep finding other blog posts about static state. This post by Mark Dominus warns against keeping object information outside of the object. He does so in an almost embarrassingly reasonable way. Though he doesn't mention static state, he does argue against global state, of which static state is merely a milder version.