Hacker News new | ask | show | jobs
by edwardio 4360 days ago
It's not that awkward, C# does the same thing:

   interface IPoint
    {
   // Property signatures: 
   int x
   {
      get;
      set;
   }

   int y
   {
      get;
      set;
   }
   }
(taken from http://msdn.microsoft.com/en-us/library/87d83y5b.aspx)
1 comments

I don't see the similarity. The Ceylon feature I find awkard is that class definitions have two parts (initializer/declaration) with different semantics, without explicit demarcation.

In the example above, this code:

    class Point() {
        Float x => y;
        Float y => x;
    }
is fine, because both declarations are parsed as being in the declaration section. However, when you add another line after that:

    class Point() {
        Float x => y;  //compiler error: y is not referenceable
        Float y => x;
        Float->Float xy = x->y;
    }
The original two declarations are now part of the initializer section, because the third line is a statement that is not allowed in the declaration section. This changes the semantics of the first two lines.
Seems like a bug.
FTR, the whole declaration section vs initialization section stuff really boils down to this:

    You can't declare mutually-recursive methods before 
    initialization logic in the body of a class. You have to 
    declare them _after_ the fields of the class are fully 
    initialized.
That's not so hard to understand.

To see why it's like this, you need to consider :

- Unlike most OO languages Ceylon statically _guarantees_ that a field of an object is initialized before it is accessed. (If we didn't, then we would have to have something conceptually equivalent to a NullPointerException when you access a field before initialization.) - We've always hated the amazingly repeaty syntax of constructors in C++/C#/Java.

What I mean is, in Java I have to write stuff like:

    class Point {
        final float x; 
        final float y;
        Point(float x, float y) {
            this.x = x;
            this.y = y;
        }
    }
In Ceylon, it would be:

    class Point(Float x, Float y) {}
And even with all that verbosity in the Java version, I _still_ don't get a static guarantee that `x` and `y` are initialized before they are used by a method. This code prints `0.0`:

    class Point {
        final float x; 
        final float y;
        Point(float x, float y) {
            fun();
            this.x = x;
            this.y = y;
        }
        void fun() { System.out.println(x); }
        public static final void main(String[] args) {
            new Point(1.0f,2.0f);
        }
    }
So Ceylon wants to solve _both_ these problems: eliminate the verbosity, and provide the static checking for initialization. Now, this is an _almost_ completely transparent process. I almost never find myself thinking about declaration section vs initialization section. I just write classes so that fields are initialized before the methods that use them are declared. Which is basically super-intuitive. In >95% of classes that's all you need to know. Very rarely, I mix this up, and the compiler complains and I need to move a member up or down. Which takes seconds to fix. Very, very rarely, I have a class with some kind of self-recursive initialization logic and then only in that case do I need to start holding the mental model of declaration-vs-initialization sections in my head. In practice, I think I've only run into this situation _once_, and it took me a few minutes to restructure the code. (FTR, it was `ArrayList` in `ceylon.collection`.)