Thread overview | ||||||
---|---|---|---|---|---|---|
|
September 18, 2005 initializers | ||||
---|---|---|---|---|
| ||||
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 Re: initializers | ||||
---|---|---|---|---|
| ||||
Posted in reply to John C | 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 Re: initializers | ||||
---|---|---|---|---|
| ||||
Posted in reply to Chris Sauls | "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 Re: initializers | ||||
---|---|---|---|---|
| ||||
Posted in reply to John C | 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
|
Copyright © 1999-2021 by the D Language Foundation