Thread overview
initializers
Sep 18, 2005
John C
Sep 19, 2005
Chris Sauls
Sep 19, 2005
John C
Sep 19, 2005
Chris Sauls
September 18, 2005
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.


September 19, 2005
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
September 19, 2005
"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.


September 19, 2005
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