Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perform 'late' value initialization non-polymorphically, *sometimes* #11

Open
jvasileff opened this issue Apr 1, 2016 · 4 comments
Open

Comments

@jvasileff
Copy link
Owner

The most reasonable (edit: or not... see below) output for the code below is what the JVM produces, where the assignment to a late value is non-polymorphic unless the value has a late or variable refinement.

I'm guessing the JVM behavior is a natural result of setX() refinements existing for j and k, but not i.

class A() {
    shared default late Integer i;
    shared default late Integer j;
    shared default late Integer k;

    shared void initialize(Integer x) {
        i = x;
        j = x;
        k = x;
    }
}

class B() extends A() {
    shared actual Integer i = 1;
    shared actual variable Integer j = 1;
    shared actual late Integer k;
}

shared void runit() {
    value b = B();
    b.initialize(2);
    printAll { b.i, b.j, b.k };
    // {1, 2, 2}      on JVM
    // {1, 1, Crash!} on JS
    // {2, 2, 2}      on Dart
}

OTOH, @gavinking, should each of these be allowed?

@jvasileff
Copy link
Owner Author

Of course, when they are initialized polymorphically, there is no way to initialize the refined value. So, within B, super.j and super.k will always throw an InitializationError.

@jvasileff
Copy link
Owner Author

Actually, the results on the JVM are inconsistent too, when the late field is covariantly refined!

class AA() {
    shared default late Object i;
    shared default late Object j;

    shared void initialize(Object x) {
        this.i = x;
        this.j = x;
    }
}

class BB() extends AA() {
    shared actual variable Object i = "1";
    shared actual variable String j = "1"; 
}

shared void runX() {
    value b1 = BB();
    b1.initialize(2);
    printAll { b1.i, b1.j };
    // 2, 1 on JVM
    // 1, 1 on JS
    // 2, 2 on Dart
}

And initializing refined late attributes is impossible on the JVM:

class AAA() {
    shared default late Object i;

    shared default void initialize(Object x) {
        this.i = x;
    }
}

class BBB() extends AAA() {
    shared actual late Object i;

    shared actual void initialize(Object x) {
        super.initialize(1);
        this.i = x;
        // JVM ERROR: ceylon.language.InitializationError "Re-initialization of 'late' attribute"
    }

    shared Object superI => super.i;
}

shared void run() {
    value b1 = BBB();
    b1.initialize(2);
    print(b1.i); // 2
    print(b1.superI);
    // JVM: ERROR: ceylon.language.InitializationError "Accessing uninitialized 'late' attribute 'i'"
    // JS: 1
}

@jvasileff
Copy link
Owner Author

jvasileff commented Apr 2, 2016

A couple options:

Option A

  • late, late variable: non-default, so no problem.
  • default late: disallow.
  • default late variable: polymorphic assignment. How do we initialized the refined value? Do we care?

Option B

Same as A), but allow default late with non-polymorphic assignments. If refined by a late or a variable late, the refinement must always handle its own initialization.

I'm currently leaning towards B), which pretty much matches the current JS behavior.

@jvasileff
Copy link
Owner Author

jvasileff commented Apr 2, 2016

To be clear, we know that

shared default Integer x = 0;

"non-polymorphically" initializes x. It does not assign 0 to a potential refinement of x. super.x from within a subclass will be 0, while someInstance.x may not be 0 if x is refined.

It seems natural that the following would be exactly the same:

shared default late Integer x;
x = 0;

But, when x is variable:

shared default late variable Integer x;
x = 0;

x = 0 is ambiguous. Is it meant to initialize x, as in the previous examples? Or is it a normal assignment to the variable x, which, at least for non-late values, is polymorphic? The latter seems to be the reasonable choice. By annotating with variable, you forgo the ability to perform normal initialization on the value.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant