www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - initializers

reply "John C" <johnch_atms hotmail.com> writes:
I know array initializers are coming post-1.0, but how about allowing 
array-like initialization on array-like classes - specifically, classes that 
implement opIndex/opIndexAssign as well as a contructor that specifies the 
size of its elements.

Example:

    class List(T) {
        this(int size) ...
        T opIndex(int index) ...
        void opIndexAssign(T val, int index) ...
    }

    List!(int) list = [ 1, 12, 123 ];

or perhaps

    List!(int) list = new List!(int) [ 1, 12, 123 ];

This would be the equivalent of:

    List!(int) list = new List!(int)(3);
    list[0] = 1; list[1] = 12; list[2] = 123;

While a template could do the initialization, the syntax is more consistent 
with how arrays will work.

Also, when/if non-static struct initializers are implemented, perhaps object 
initializers could be added to the language.

Example:

    enum Genre { ROCK, POP, CLASSICAL }

    class Song {
        private Genre genre_;
        char[] title;
        char[] artist;
        Genre genre() { return genre_; }
        void genre(Genre val) { genre_ = val; }
    }

    Song song2 = { title: "Song2", artist: "Blur", genre: Genre.ROCK }; // 
should work only for public properties and fields

Which would be the same as this:

    Song song2 = new Song;
    song2.title = "Song2"; song2.artist = "Blur"; song2.genre = Genre.ROCK;

Now, obviously a class constructor could do the work, but most classes don't 
have constructors for every field/property.

Join both concepts together and you have a very nice syntax for creating 
collections.

    List!(Song) compilation = [
        new Song {
            title = "Speed of Sound",
            artist = "Coldplay",
            genre = Genre.ROCK
        },
        new Song {
            title = "King Tide",
            artist = "Neil Finn",
            genre = Genre.POP
        }
    ];

No sassy comments about my taste in music, but feel free to tear this idea 
down. 
Sep 18 2005
parent reply Chris Sauls <ibisbasenji gmail.com> writes:
John C wrote:
 I know array initializers are coming post-1.0, but how about allowing 
 array-like initialization on array-like classes - specifically, classes that 
 implement opIndex/opIndexAssign as well as a contructor that specifies the 
 size of its elements.
 
 Example:
 
     class List(T) {
         this(int size) ...
         T opIndex(int index) ...
         void opIndexAssign(T val, int index) ...
     }
 
     List!(int) list = [ 1, 12, 123 ];
 
 or perhaps
 
     List!(int) list = new List!(int) [ 1, 12, 123 ];
 
 This would be the equivalent of:
 
     List!(int) list = new List!(int)(3);
     list[0] = 1; list[1] = 12; list[2] = 123;
 
 While a template could do the initialization, the syntax is more consistent 
 with how arrays will work.

I do see your idea, and it is kind of nifty... But I see two things. The first, lesser, argument is that it could be provided (yes I know, this doesn't make it universal) via a static instantiator method using typesafe variadics. Aka, something like this: # class List(T) { # static List(T) make (T elems[]...) { # foreach (size_t i, T x; elems) # // ... # } # } # # List!(int) list = List!(int).make(1, 12, 123); Or you could be really cute and make it a static opCall overload, leading to this syntax: # List!(int) list = List!(int)(1, 12, 123); And you could also use a static opIndex overload to get a syntax somewhat like your proposal: # List!(int) list = List!(int)[1, 12, 123]; Although now I'm just being rediculous. The second, greater, argument is that, frankly, how should the compiler determine whether or not this syntax is semantically valid for a given class? You listed three (really two) requirements. 1 - opIndex/opIndexAssign overloads 2 - a constructor that takes a size Number 1 is easy as pie (easier, even) to check for! But number two is the problem. How does the compiler know that a given constructor taking an integral parameter is in fact a size/length/capacity setting constructor? The only way I can think of is by decoration, but that means adding a new attribute that really only serves a single, small, uncommonly used feature.
 Also, when/if non-static struct initializers are implemented, perhaps object 
 initializers could be added to the language.
 
 Example:
 
     enum Genre { ROCK, POP, CLASSICAL }
 
     class Song {
         private Genre genre_;
         char[] title;
         char[] artist;
         Genre genre() { return genre_; }
         void genre(Genre val) { genre_ = val; }
     }
 
     Song song2 = { title: "Song2", artist: "Blur", genre: Genre.ROCK }; // 
 should work only for public properties and fields
 
 Which would be the same as this:
 
     Song song2 = new Song;
     song2.title = "Song2"; song2.artist = "Blur"; song2.genre = Genre.ROCK;
 
 Now, obviously a class constructor could do the work, but most classes don't 
 have constructors for every field/property.
 
 Join both concepts together and you have a very nice syntax for creating 
 collections.
 
     List!(Song) compilation = [
         new Song {
             title = "Speed of Sound",
             artist = "Coldplay",
             genre = Genre.ROCK
         },
         new Song {
             title = "King Tide",
             artist = "Neil Finn",
             genre = Genre.POP
         }
     ];
 

Now this, in general, actually does look interesting/promising. There should be a way to accomplish something like this. -- Chris Sauls
Sep 19 2005
parent reply "John C" <johnch_atms hotmail.com> writes:
"Chris Sauls" <ibisbasenji gmail.com> wrote in message 
news:dgls4u$160m$1 digitaldaemon.com...
 John C wrote:
 I know array initializers are coming post-1.0, but how about allowing 
 array-like initialization on array-like classes - specifically, classes 
 that implement opIndex/opIndexAssign as well as a contructor that 
 specifies the size of its elements.

 Example:

     class List(T) {
         this(int size) ...
         T opIndex(int index) ...
         void opIndexAssign(T val, int index) ...
     }

     List!(int) list = [ 1, 12, 123 ];

 or perhaps

     List!(int) list = new List!(int) [ 1, 12, 123 ];

 This would be the equivalent of:

     List!(int) list = new List!(int)(3);
     list[0] = 1; list[1] = 12; list[2] = 123;

 While a template could do the initialization, the syntax is more 
 consistent with how arrays will work.

I do see your idea, and it is kind of nifty... But I see two things. The first, lesser, argument is that it could be provided (yes I know, this doesn't make it universal) via a static instantiator method using typesafe variadics. Aka, something like this: # class List(T) { # static List(T) make (T elems[]...) { # foreach (size_t i, T x; elems) # // ... # } # } # # List!(int) list = List!(int).make(1, 12, 123); Or you could be really cute and make it a static opCall overload, leading to this syntax: # List!(int) list = List!(int)(1, 12, 123); And you could also use a static opIndex overload to get a syntax somewhat like your proposal: # List!(int) list = List!(int)[1, 12, 123]; Although now I'm just being rediculous. The second, greater, argument is that, frankly, how should the compiler determine whether or not this syntax is semantically valid for a given class? You listed three (really two) requirements. 1 - opIndex/opIndexAssign overloads 2 - a constructor that takes a size Number 1 is easy as pie (easier, even) to check for! But number two is the problem. How does the compiler know that a given constructor taking an integral parameter is in fact a size/length/capacity setting constructor? The only way I can think of is by decoration, but that means adding a new attribute that really only serves a single, small, uncommonly used feature.

What if the class had to derive from a standard interface declaring those functions? There's a precedent for this: IUnknown, which the compiler treats specially.
 Also, when/if non-static struct initializers are implemented, perhaps 
 object initializers could be added to the language.

 Example:

     enum Genre { ROCK, POP, CLASSICAL }

     class Song {
         private Genre genre_;
         char[] title;
         char[] artist;
         Genre genre() { return genre_; }
         void genre(Genre val) { genre_ = val; }
     }

     Song song2 = { title: "Song2", artist: "Blur", genre: Genre.ROCK }; 
 // should work only for public properties and fields

 Which would be the same as this:

     Song song2 = new Song;
     song2.title = "Song2"; song2.artist = "Blur"; song2.genre = 
 Genre.ROCK;

 Now, obviously a class constructor could do the work, but most classes 
 don't have constructors for every field/property.

 Join both concepts together and you have a very nice syntax for creating 
 collections.

     List!(Song) compilation = [
         new Song {
             title = "Speed of Sound",
             artist = "Coldplay",
             genre = Genre.ROCK
         },
         new Song {
             title = "King Tide",
             artist = "Neil Finn",
             genre = Genre.POP
         }
     ];

Now this, in general, actually does look interesting/promising. There should be a way to accomplish something like this. -- Chris Sauls

Yes, the main thing is to make it easier to create lists. Most languages (apart from C/C++ and their derivatives) have some kind of built-in support for list initialization.
Sep 19 2005
parent Chris Sauls <ibisbasenji gmail.com> writes:
John C wrote:
 "Chris Sauls" <ibisbasenji gmail.com> wrote in message 
 news:dgls4u$160m$1 digitaldaemon.com...
 
Number 1 is easy as pie (easier, even) to check for!  But number two is 
the problem.  How does the compiler know that a given constructor taking 
an integral parameter is in fact a size/length/capacity setting 
constructor?  The only way I can think of is by decoration, but that means 
adding a new attribute that really only serves a single, small, uncommonly 
used feature.

What if the class had to derive from a standard interface declaring those functions? There's a precedent for this: IUnknown, which the compiler treats specially.

I hadn't thought of that, but it could work. Something like: # interface IStdArray(T) { # this(size_t); # T opIndex(size_t); # T opIndexAssign(T, size_t); # } # # interface IStdAssoc(K, V) { # this(size_t); # V opIndex(K); # V opIndexAssign(V, K); # } -- Chris Sauls
Sep 19 2005