Comment #0 by thelastmammoth — 2012-04-30T14:04:10Z
Currently, array literals such as
auto x=[1,2,3];
make x dynamic. To get a static array one needs to write:
int[3] x=[1,2,3];
which is inconvenient for many reasons:
* DRY principle (need to explicitly write 3 as the length and specify the type int)
* no easy way to pass a static array litteral to a function accepting a static array; for example it requires:
int[3] x=[1,2,3];
fun(x);
Wouldn't it be simple to allow writing array literals using the syntax:
auto x=[1,2,3]S;
where S stands for static?
More generally the compiler should translate [x1,...,xn]S to: typeof(x1)[n]
Advantages:
* static array litterals becomes as convenient as dynamic ones
* no confusion possible for the compiler; I believe this syntax doesn't clash with existing syntax.
* In our previous example, no need to write an intermediate x: we can just write
fun([1,2,3]S);
or
fun([1.0,2,3]S); //for example, if static array of doubles requested
* this would also prevent the common workaround hacks of the form:
void fun(T...)(T x){} which accept fun(1,2,3): one could just write:
void fun(T,uint N)(in T[N]x){} or void fun(T,uint N)(T[N]x){}
* this could prevent inefficient intermediate code as reported in Issue 2356 and related, as it would be clear from "S" that a static is requested.
* this could be used in expressions as well: auto x=[1,2,3]S+[4,5,6]S;
This should be simpler than a previous request I've seen for int[$]x=[1,2,3];, which still requires one to write the type explicitly.
Comment #1 by bearophile_hugs — 2012-04-30T14:19:21Z
Aesthetically-wise this new literal syntax is not so good, but it solves a problem, I think it doesn't introduce problems, and it's effective, so it's an interesting idea:
auto a1 = [1, 2, 3]s;
auto a2 = [1, 2, 3]S;
auto s1 = "abc"s;
auto a3 = ["abc"ds, "def"ds]s; // dchar[3][2] ?
A problem is that "static array" is not a correct term in D, because here foo is a static array but it's not what you are writing about:
void main() {
static foo = [1, 2];
}
So I generally call them fixed-sized arrays. So an alternative is to use a different letter to denote them (I don't think they get confused with floats):
auto a1 = [1, 2, 3]f;
auto a2 = [1, 2, 3]f;
auto s1 = "abc"f;
auto a3 = ["abc"f, "def"f]f;
The [$] was shown in Issue 481 and elsewhere.
Comment #2 by thelastmammoth — 2012-05-30T13:03:09Z
Thanks for the reply, I wish this could receive more attention...
If you're worried about esthetics, how about using a distinct symbol instead of a letter to make it pop out:
For example:
auto x=[1,2,3]@;
fun([1,2,3]@);
Comment #3 by samjnaa — 2013-05-31T10:18:19Z
(In reply to comment #2)
> Thanks for the reply, I wish this could receive more attention...
> If you're worried about esthetics, how about using a distinct symbol instead of
> a letter to make it pop out:
> For example:
> auto x=[1,2,3]@;
> fun([1,2,3]@);
I also don't like [,,,]S syntax. But i.o. @ I think we can re-use the $ which is suggested for specifying in the type as in bug 481.
But even then, such a special syntax would only be needed if it is necessary for specifying in a literal that a fixed-size array is intended. In assignment or other expressions it would not be required but only in direct function arguments.
And even then, why wouldn't a function that expects a fixedsize array not accept a dynamic one of appropriate size? If it doesn't accept, then probably that is what should be fixed and no new syntax for such fixed-size array literals is needed.
Comment #4 by andrej.mitrovich — 2013-05-31T10:27:09Z
(In reply to comment #0)
> * no easy way to pass a static array literal to a function accepting a static
> array; for example it requires:
> int[3] x=[1,2,3];
> fun(x);
That can't be true. This works fine:
-----
void fun(int[3] arr) { }
void main()
{
fun([1, 2, 3]);
}
-----
I've tried it with 2.050, and it worked there.
Additionally since 2.063 you can also do:
-----
void fun(int[3] arr) { }
void main()
{
auto x = [1, 2, 3];
fun(x[0 .. 3]);
}
-----
Comment #5 by nick — 2014-12-16T13:43:22Z
Is there an advantage of literal syntax vs. a template function staticArray(1, 2)?
Comment #6 by bearophile_hugs — 2014-12-16T14:21:48Z
(In reply to Nick Treleaven from comment #5)
> Is there an advantage of literal syntax vs. a template function
> staticArray(1, 2)?
There are various small advantages. A template function like that seems to generate template bloat.
It's also longer to write:
[[1,2]s, [1,2]s, [1,2]s, [1,2]s]
versus:
[staticArray(1,2), staticArray(1,2), staticArray(1,2), staticArray(1,2)]
And probably []s is also faster to compile by the compiler.
Also, staticArray returns a fixed-size array, that according to the D ABI is a pointer, and the compiler has to inline the call to staticArray to regain efficiency. By default the D compiler inlines only if you use -inline. The []s lacks this problem and is efficient at all compilation levels.
The []s will probably become safe to use once D has some kind of tracking of memory ownership.
There are also some disadvantages, like a little more complexity in the D language.
Comment #7 by bearophile_hugs — 2014-12-27T13:22:24Z
If you want to generate certain i,i+10 pairs you can use this, but it generates lot of not efficient allocations:
void main() @nogc {
import std.range: iota;
import std.algorithm: map;
auto pairs = 10.iota.map!(i => [i, i + 10]);
foreach (p; pairs) {} // Isn't @nogc.
}
You can solve it using a little wrapper struct, but the result isn't an array despite the alias this:
void main() @nogc {
import std.range: iota;
import std.algorithm: map;
static struct Pair { int[2] a; alias a this; }
auto pairs = 10.iota.map!(i => Pair([i, i + 10]));
foreach (p; pairs) {} // OK
}
You can also use this, but casts are unsafe things and it's better to minimize their usage:
void main() @nogc {
import std.range: iota;
import std.algorithm: map;
auto pairs = 10.iota.map!(i => cast(int[2])[i, i + 10]);
foreach (p; pairs) {} // OK
}
the []s syntax is handy to solve the problem, avoiding to create and manage a new type and keeping all safety:
void main() @nogc {
import std.range: iota;
import std.algorithm: map;
auto pairs = 10.iota.map!(i => [i, i + 10]s);
foreach (p; pairs) {} // OK
}
Comment #8 by nick — 2014-12-27T17:02:06Z
(In reply to bearophile_hugs from comment #6)
> (In reply to Nick Treleaven from comment #5)
> > Is there an advantage of literal syntax vs. a template function
> > staticArray(1, 2)?
>
> There are various small advantages. A template function like that seems to
> generate template bloat.
Yes, but this is already pervasive in Phobos.
> It's also longer to write:
>
> [[1,2]s, [1,2]s, [1,2]s, [1,2]s]
>
> versus:
>
> [staticArray(1,2), staticArray(1,2), staticArray(1,2), staticArray(1,2)]
alias s = staticArray;
> And probably []s is also faster to compile by the compiler.
Probably not noticeably.
> Also, staticArray returns a fixed-size array, that according to the D ABI is
> a pointer, and the compiler has to inline the call to staticArray to regain
> efficiency. By default the D compiler inlines only if you use -inline. The
> []s lacks this problem and is efficient at all compilation levels.
This is another general problem. Inlining small template functions by default might be nice, if it doesn't slow the compiler too much, and could help avoid template bloat. But passing -inline isn't too much to ask IMO.
Comment #9 by rswhite4 — 2014-12-27T18:16:37Z
> the []s syntax is handy to solve the problem, avoiding to create and manage
> a new type and keeping all safety:
>
> void main() @nogc {
> import std.range: iota;
> import std.algorithm: map;
> auto pairs = 10.iota.map!(i => [i, i + 10]s);
> foreach (p; pairs) {} // OK
> }
What do you think about:
----
import std.stdio;
auto ref U[N] s(U, alias N)(auto ref U[N] arr) pure nothrow @safe @property @nogc
if (__traits(compiles, { size_t i = N; }))
{
return arr;
}
void test1(T)(T[3] arr) {
}
void test2(T)(auto ref T[3] arr) {
}
void main() {
auto a = s([1, 2, 3]);
writeln(typeid(a));
auto b = [1, 2, 3].s;
writeln(typeid(b));
test1([1, 2, 3]);
test1([1, 2, 3].s);
test2([1, 2, 3].s);
}
----
Comment #10 by bearophile_hugs — 2014-12-27T19:16:22Z
(In reply to rswhite4 from comment #9)
> auto ref U[N] s(U, alias N)(auto ref U[N] arr) pure nothrow @safe @property
> @nogc
> if (__traits(compiles, { size_t i = N; }))
Simpler:
auto ref U[N] s(U, size_t N)(auto ref U[N] arr)
pure nothrow @safe @property @nogc {
return arr;
}
Comment #11 by rswhite4 — 2014-12-27T19:19:13Z
(In reply to bearophile_hugs from comment #10)
> (In reply to rswhite4 from comment #9)
>
> > auto ref U[N] s(U, alias N)(auto ref U[N] arr) pure nothrow @safe @property
> > @nogc
> > if (__traits(compiles, { size_t i = N; }))
>
> Simpler:
>
> auto ref U[N] s(U, size_t N)(auto ref U[N] arr)
> pure nothrow @safe @property @nogc {
> return arr;
> }
Of Course! Sometimes I think way too complicated.
Comment #12 by bearophile_hugs — 2014-12-27T19:23:25Z
(In reply to Nick Treleaven from comment #8)
> Yes, but this is already pervasive in Phobos.
I suggest to not make the situation worse.
> Probably not noticeably.
I don't believe this much. They are all templates with different length and type, and array literals are everywhere.
> Inlining small template functions by default might be nice,
This ER isn't asking for a template function.
Don't try to replace features that should be built-in with tons of templates, you end like the std.typecons.Tuple fiasco.
Comment #13 by nick — 2014-12-28T16:56:48Z
The point is the templates can be inlined, and the bloat *could* be removed by the optimizer.
Comment #14 by bearophile_hugs — 2014-12-28T17:07:55Z
(In reply to Nick Treleaven from comment #13)
> The point is the templates can be inlined, and the bloat *could* be removed
> by the optimizer.
I agree.
Comment #15 by nick — 2016-04-27T11:31:41Z
Added link to Phobos pull, but that has just been closed because there's no solution to Issue 12625 yet.
Comment #16 by robert.schadek — 2024-12-13T17:59:39Z