www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - static arrays becoming value types

reply Walter Bright <newshound1 digitalmars.com> writes:
Currently, static arrays are (as in C) half-value types and 
half-reference types. This tends to cause a series of weird problems and 
special cases in the language semantics, such as functions not being 
able to return static arrays, and out parameters not being possible to 
be static arrays.

Andrei and I agonized over this for some time, and eventually came to 
the conclusion that static arrays should become value types. I.e.,

   T[3]

should behave much as if it were:

   struct ??
   {
      T[3];
   }

Then it can be returned from a function. In particular,

   void foo(T[3] a)

is currently done (as in C) by passing a pointer to the array, and then 
with a bit of compiler magic 'a' is rewritten as (*a)[3]. Making this 
change would mean that the entire array would be pushed onto the 
parameter stack, i.e. a copy of the array, rather than a reference to it.

Making this change would clean up the internal behavior of types. 
They'll be more orthogonal and consistent, and templates will work better.

The previous behavior for function parameters can be retained by making 
it a ref parameter:

    void foo(ref T[3] a)
Oct 19 2009
next sibling parent reply Jason House <jason.james.house gmail.com> writes:
Walter Bright Wrote:

 Currently, static arrays are (as in C) half-value types and 
 half-reference types. This tends to cause a series of weird problems and 
 special cases in the language semantics, such as functions not being 
 able to return static arrays, and out parameters not being possible to 
 be static arrays.
 
 Andrei and I agonized over this for some time, and eventually came to 
 the conclusion that static arrays should become value types. I.e.,
 
    T[3]
 
 should behave much as if it were:
 
    struct ??
    {
       T[3];
    }
 
 Then it can be returned from a function. In particular,
 
    void foo(T[3] a)
 
 is currently done (as in C) by passing a pointer to the array, and then 
 with a bit of compiler magic 'a' is rewritten as (*a)[3]. Making this 
 change would mean that the entire array would be pushed onto the 
 parameter stack, i.e. a copy of the array, rather than a reference to it.
 
 Making this change would clean up the internal behavior of types. 
 They'll be more orthogonal and consistent, and templates will work better.
 
 The previous behavior for function parameters can be retained by making 
 it a ref parameter:
 
     void foo(ref T[3] a)
I've never heard the argument why they should be value types. Can you or Andrei explain why it makes more sense as value types?
Oct 19 2009
parent grauzone <none example.net> writes:
Jason House wrote:
 Walter Bright Wrote:
 
 Currently, static arrays are (as in C) half-value types and 
 half-reference types. This tends to cause a series of weird problems and 
 special cases in the language semantics, such as functions not being 
 able to return static arrays, and out parameters not being possible to 
 be static arrays.

 Andrei and I agonized over this for some time, and eventually came to 
 the conclusion that static arrays should become value types. I.e.,

    T[3]

 should behave much as if it were:

    struct ??
    {
       T[3];
    }

 Then it can be returned from a function. In particular,

    void foo(T[3] a)

 is currently done (as in C) by passing a pointer to the array, and then 
 with a bit of compiler magic 'a' is rewritten as (*a)[3]. Making this 
 change would mean that the entire array would be pushed onto the 
 parameter stack, i.e. a copy of the array, rather than a reference to it.

 Making this change would clean up the internal behavior of types. 
 They'll be more orthogonal and consistent, and templates will work better.

 The previous behavior for function parameters can be retained by making 
 it a ref parameter:

     void foo(ref T[3] a)
I've never heard the argument why they should be value types. Can you or Andrei explain why it makes more sense as value types?
Because they are some bastardization now. Neither slice, nor reference type, nor value type. Somehow, they behave like value type when declared in a struct or a variable. (Because they allocate memory for the contents they point to.) But if you pass them as argument, they behave like reference type (they're just pointers to the actual data). And you can't return them at all from functions for unknown reasons. It's a real WTF. The only clean way to fix them is the as described by Walter.
Oct 19 2009
prev sibling next sibling parent reply "Robert Jacques" <sandford jhu.edu> writes:
On Mon, 19 Oct 2009 21:50:46 -0400, Walter Bright  
<newshound1 digitalmars.com> wrote:
 Currently, static arrays are (as in C) half-value types and  
 half-reference types. This tends to cause a series of weird problems and  
 special cases in the language semantics, such as functions not being  
 able to return static arrays, and out parameters not being possible to  
 be static arrays.

 Andrei and I agonized over this for some time, and eventually came to  
 the conclusion that static arrays should become value types. I.e.,

    T[3]

 should behave much as if it were:

    struct ??
    {
       T[3];
    }

 Then it can be returned from a function. In particular,

    void foo(T[3] a)

 is currently done (as in C) by passing a pointer to the array, and then  
 with a bit of compiler magic 'a' is rewritten as (*a)[3]. Making this  
 change would mean that the entire array would be pushed onto the  
 parameter stack, i.e. a copy of the array, rather than a reference to it.

 Making this change would clean up the internal behavior of types.  
 They'll be more orthogonal and consistent, and templates will work  
 better.

 The previous behavior for function parameters can be retained by making  
 it a ref parameter:

     void foo(ref T[3] a)
Thank you. Will the various operators be overloaded? i.e. a = b + c; instead of a[] = b[] + c[]; ?
Oct 19 2009
parent Walter Bright <newshound1 digitalmars.com> writes:
Robert Jacques wrote:
 Thank you. Will the various operators be overloaded? i.e. a = b + c; 
 instead of a[] = b[] + c[]; ?
Don't know. For now, I'd just leave the a=b+c syntax illegal. We can always loosen things up later.
Oct 19 2009
prev sibling next sibling parent reply dsimcha <dsimcha yahoo.com> writes:
== Quote from Walter Bright (newshound1 digitalmars.com)'s article
 Currently, static arrays are (as in C) half-value types and
 half-reference types. This tends to cause a series of weird problems and
 special cases in the language semantics, such as functions not being
 able to return static arrays, and out parameters not being possible to
 be static arrays.
 Andrei and I agonized over this for some time, and eventually came to
 the conclusion that static arrays should become value types. I.e.,
    T[3]
 should behave much as if it were:
    struct ??
    {
       T[3];
    }
 Then it can be returned from a function. In particular,
    void foo(T[3] a)
 is currently done (as in C) by passing a pointer to the array, and then
 with a bit of compiler magic 'a' is rewritten as (*a)[3]. Making this
 change would mean that the entire array would be pushed onto the
 parameter stack, i.e. a copy of the array, rather than a reference to it.
 Making this change would clean up the internal behavior of types.
 They'll be more orthogonal and consistent, and templates will work better.
 The previous behavior for function parameters can be retained by making
 it a ref parameter:
     void foo(ref T[3] a)
Vote++. It's funny, I use static arrays so little that I never realized that they weren't passed by value to functions. I'd absolutely love to be able to just return static arrays from functions, and often use structs to do that now, but using structs feels like a really ugly hack.
Oct 19 2009
parent reply Leandro Lucarella <llucax gmail.com> writes:
dsimcha, el 20 de octubre a las 02:44 me escribiste:
 == Quote from Walter Bright (newshound1 digitalmars.com)'s article
 Currently, static arrays are (as in C) half-value types and
 half-reference types. This tends to cause a series of weird problems and
 special cases in the language semantics, such as functions not being
 able to return static arrays, and out parameters not being possible to
 be static arrays.
 Andrei and I agonized over this for some time, and eventually came to
 the conclusion that static arrays should become value types. I.e.,
    T[3]
 should behave much as if it were:
    struct ??
    {
       T[3];
    }
 Then it can be returned from a function. In particular,
    void foo(T[3] a)
 is currently done (as in C) by passing a pointer to the array, and then
 with a bit of compiler magic 'a' is rewritten as (*a)[3]. Making this
 change would mean that the entire array would be pushed onto the
 parameter stack, i.e. a copy of the array, rather than a reference to it.
 Making this change would clean up the internal behavior of types.
 They'll be more orthogonal and consistent, and templates will work better.
 The previous behavior for function parameters can be retained by making
 it a ref parameter:
     void foo(ref T[3] a)
Vote++. It's funny, I use static arrays so little that I never realized that they weren't passed by value to functions. I'd absolutely love to be able to just return static arrays from functions, and often use structs to do that now, but using structs feels like a really ugly hack.
It would be the poor men tuple for returning (homogeneous) stuff =P -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- La máquina de la moneda, mirá como te queda! -- Sidharta Kiwi
Oct 20 2009
parent reply language_fan <foo bar.com.invalid> writes:
Tue, 20 Oct 2009 10:34:35 -0300, Leandro Lucarella thusly wrote:

 dsimcha, el 20 de octubre a las 02:44 me escribiste:
 == Quote from Walter Bright (newshound1 digitalmars.com)'s article
 Currently, static arrays are (as in C) half-value types and
 half-reference types. This tends to cause a series of weird problems
 and special cases in the language semantics, such as functions not
 being able to return static arrays, and out parameters not being
 possible to be static arrays.
 Andrei and I agonized over this for some time, and eventually came to
 the conclusion that static arrays should become value types. I.e.,
    T[3]
 should behave much as if it were:
    struct ??
    {
       T[3];
    }
 Then it can be returned from a function. In particular,
    void foo(T[3] a)
 is currently done (as in C) by passing a pointer to the array, and
 then with a bit of compiler magic 'a' is rewritten as (*a)[3]. Making
 this change would mean that the entire array would be pushed onto the
 parameter stack, i.e. a copy of the array, rather than a reference to
 it. Making this change would clean up the internal behavior of types.
 They'll be more orthogonal and consistent, and templates will work
 better. The previous behavior for function parameters can be retained
 by making it a ref parameter:
     void foo(ref T[3] a)
Vote++. It's funny, I use static arrays so little that I never realized that they weren't passed by value to functions. I'd absolutely love to be able to just return static arrays from functions, and often use structs to do that now, but using structs feels like a really ugly hack.
It would be the poor men tuple for returning (homogeneous) stuff =P
It depends on how you define things. Traditionally tuples are seen as a generalization of pairs (2 elements -> n elements). Records, on the other hand, are generalization of tuples (simple number index -> named elements). You need couple of additional layers of generalization to come up with structs (subtyping, member functions, generics etc.) One nasty thing about D's structs is that they don't have structural equivalence relation unlike tuples. So you need to use the same container struct type to get the same semantics. To achieve that you would need some kind of STuple on standard library level or other kinds of hacks. What I find unfortunate in D is that your abstractions come in two sizes - either you use the modest tiny construct that does not scale elegantly or the enormous hammer to crush things down theatretically.
Oct 20 2009
parent reply Leandro Lucarella <llucax gmail.com> writes:
language_fan, el 20 de octubre a las 13:52 me escribiste:
 Tue, 20 Oct 2009 10:34:35 -0300, Leandro Lucarella thusly wrote:
 
 dsimcha, el 20 de octubre a las 02:44 me escribiste:
 == Quote from Walter Bright (newshound1 digitalmars.com)'s article
 Currently, static arrays are (as in C) half-value types and
 half-reference types. This tends to cause a series of weird problems
 and special cases in the language semantics, such as functions not
 being able to return static arrays, and out parameters not being
 possible to be static arrays.
 Andrei and I agonized over this for some time, and eventually came to
 the conclusion that static arrays should become value types. I.e.,
    T[3]
 should behave much as if it were:
    struct ??
    {
       T[3];
    }
 Then it can be returned from a function. In particular,
    void foo(T[3] a)
 is currently done (as in C) by passing a pointer to the array, and
 then with a bit of compiler magic 'a' is rewritten as (*a)[3]. Making
 this change would mean that the entire array would be pushed onto the
 parameter stack, i.e. a copy of the array, rather than a reference to
 it. Making this change would clean up the internal behavior of types.
 They'll be more orthogonal and consistent, and templates will work
 better. The previous behavior for function parameters can be retained
 by making it a ref parameter:
     void foo(ref T[3] a)
Vote++. It's funny, I use static arrays so little that I never realized that they weren't passed by value to functions. I'd absolutely love to be able to just return static arrays from functions, and often use structs to do that now, but using structs feels like a really ugly hack.
It would be the poor men tuple for returning (homogeneous) stuff =P
It depends on how you define things. Traditionally tuples are seen as a generalization of pairs (2 elements -> n elements). Records, on the other
In what tradition? C++ maybe. I never saw a pair type outside C++, but saw tuples everywhere (even in other structured languages like SQL).
 hand, are generalization of tuples (simple number index -> named 
 elements). You need couple of additional layers of generalization to come 
 up with structs (subtyping, member functions, generics etc.)
 
 One nasty thing about D's structs is that they don't have structural 
 equivalence relation unlike tuples. So you need to use the same container 
 struct type to get the same semantics. To achieve that you would need 
 some kind of STuple on standard library level or other kinds of hacks.
 
 What I find unfortunate in D is that your abstractions come in two sizes 
 - either you use the modest tiny construct that does not scale elegantly 
 or the enormous hammer to crush things down theatretically.
I don't understand very well what are you saying anyways... -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- Fantasy is as important as wisdom
Oct 20 2009
parent reply language_fan <foo bar.com.invalid> writes:
Tue, 20 Oct 2009 12:39:47 -0300, Leandro Lucarella thusly wrote:

 language_fan, el 20 de octubre a las 13:52 me escribiste:
 Tue, 20 Oct 2009 10:34:35 -0300, Leandro Lucarella thusly wrote:
 
 dsimcha, el 20 de octubre a las 02:44 me escribiste:
 == Quote from Walter Bright (newshound1 digitalmars.com)'s article
 Currently, static arrays are (as in C) half-value types and
 half-reference types. This tends to cause a series of weird
 problems and special cases in the language semantics, such as
 functions not being able to return static arrays, and out
 parameters not being possible to be static arrays.
 Andrei and I agonized over this for some time, and eventually came
 to the conclusion that static arrays should become value types.
 I.e.,
    T[3]
 should behave much as if it were:
    struct ??
    {
       T[3];
    }
 Then it can be returned from a function. In particular,
    void foo(T[3] a)
 is currently done (as in C) by passing a pointer to the array, and
 then with a bit of compiler magic 'a' is rewritten as (*a)[3].
 Making this change would mean that the entire array would be
 pushed onto the parameter stack, i.e. a copy of the array, rather
 than a reference to it. Making this change would clean up the
 internal behavior of types. They'll be more orthogonal and
 consistent, and templates will work better. The previous behavior
 for function parameters can be retained by making it a ref
 parameter:
     void foo(ref T[3] a)
Vote++. It's funny, I use static arrays so little that I never realized that they weren't passed by value to functions. I'd absolutely love to be able to just return static arrays from functions, and often use structs to do that now, but using structs feels like a really ugly hack.
It would be the poor men tuple for returning (homogeneous) stuff =P
It depends on how you define things. Traditionally tuples are seen as a generalization of pairs (2 elements -> n elements). Records, on the other
In what tradition? C++ maybe. I never saw a pair type outside C++, but saw tuples everywhere (even in other structured languages like SQL).
Pairs are pretty common actually. You might have applications that have mappings, functions, or zip (list operation) etc. I admit these are more common in functional languages but the main reason for this is that most mainstream languages do not support the Pair or Tuple types in any way. Even D has broken support (from this point of view).
 One nasty thing about D's structs is that they don't have structural
 equivalence relation unlike tuples. So you need to use the same
 container struct type to get the same semantics. To achieve that you
 would need some kind of STuple on standard library level or other kinds
 of hacks.
 
 What I find unfortunate in D is that your abstractions come in two
 sizes - either you use the modest tiny construct that does not scale
 elegantly or the enormous hammer to crush things down theatretically.
I don't understand very well what are you saying anyways...
Because of the unnecessary nominal typing in D's tuple emulation, redefinitions of Tuples do not have implicit equivalence relation: struct Tuple(T...) { T t; } struct Tuple2(T...) { T t; } void main() { Tuple!(int,int) a; Tuple!(int,int) b; Tuple2!(int,int) c; assert(a == b); // ok assert(a != c); // Error: incompatible types for ((a) != (b)) } In some other language: val a = (1,2) : [Int,Int] val b = (1,2) : [Int,Int] val c = (2,3) : [Int,Int] assert(a == b); // ok assert(a != c); // ok Did you get it now? Real tuple types do not have a special type tag which gets injected implicitly with structs. So every time you try to do something lightweight by emulating tuples, you need to refer to the global Tuple type or bang your head to the wall.
Oct 20 2009
next sibling parent reply "Robert Jacques" <sandford jhu.edu> writes:
On Tue, 20 Oct 2009 15:19:15 -0400, language_fan <foo bar.com.invalid>  
wrote:

 Tue, 20 Oct 2009 12:39:47 -0300, Leandro Lucarella thusly wrote:

 language_fan, el 20 de octubre a las 13:52 me escribiste:
 Tue, 20 Oct 2009 10:34:35 -0300, Leandro Lucarella thusly wrote:

 dsimcha, el 20 de octubre a las 02:44 me escribiste:
 == Quote from Walter Bright (newshound1 digitalmars.com)'s article
 Currently, static arrays are (as in C) half-value types and
 half-reference types. This tends to cause a series of weird
 problems and special cases in the language semantics, such as
 functions not being able to return static arrays, and out
 parameters not being possible to be static arrays.
 Andrei and I agonized over this for some time, and eventually came
 to the conclusion that static arrays should become value types.
 I.e.,
    T[3]
 should behave much as if it were:
    struct ??
    {
       T[3];
    }
 Then it can be returned from a function. In particular,
    void foo(T[3] a)
 is currently done (as in C) by passing a pointer to the array, and
 then with a bit of compiler magic 'a' is rewritten as (*a)[3].
 Making this change would mean that the entire array would be
 pushed onto the parameter stack, i.e. a copy of the array, rather
 than a reference to it. Making this change would clean up the
 internal behavior of types. They'll be more orthogonal and
 consistent, and templates will work better. The previous behavior
 for function parameters can be retained by making it a ref
 parameter:
     void foo(ref T[3] a)
Vote++. It's funny, I use static arrays so little that I never realized that they weren't passed by value to functions. I'd absolutely love to be able to just return static arrays from functions, and often use structs to do that now, but using structs feels like a really ugly hack.
It would be the poor men tuple for returning (homogeneous) stuff =P
It depends on how you define things. Traditionally tuples are seen as a generalization of pairs (2 elements -> n elements). Records, on the other
In what tradition? C++ maybe. I never saw a pair type outside C++, but saw tuples everywhere (even in other structured languages like SQL).
Pairs are pretty common actually. You might have applications that have mappings, functions, or zip (list operation) etc. I admit these are more common in functional languages but the main reason for this is that most mainstream languages do not support the Pair or Tuple types in any way. Even D has broken support (from this point of view).
 One nasty thing about D's structs is that they don't have structural
 equivalence relation unlike tuples. So you need to use the same
 container struct type to get the same semantics. To achieve that you
 would need some kind of STuple on standard library level or other kinds
 of hacks.

 What I find unfortunate in D is that your abstractions come in two
 sizes - either you use the modest tiny construct that does not scale
 elegantly or the enormous hammer to crush things down theatretically.
I don't understand very well what are you saying anyways...
Because of the unnecessary nominal typing in D's tuple emulation, redefinitions of Tuples do not have implicit equivalence relation: struct Tuple(T...) { T t; } struct Tuple2(T...) { T t; } void main() { Tuple!(int,int) a; Tuple!(int,int) b; Tuple2!(int,int) c; assert(a == b); // ok assert(a != c); // Error: incompatible types for ((a) != (b)) } In some other language: val a = (1,2) : [Int,Int] val b = (1,2) : [Int,Int] val c = (2,3) : [Int,Int] assert(a == b); // ok assert(a != c); // ok Did you get it now? Real tuple types do not have a special type tag which gets injected implicitly with structs. So every time you try to do something lightweight by emulating tuples, you need to refer to the global Tuple type or bang your head to the wall.
Or use a templated opAssign mixin to allow two desperate types to be assigned to each other. Besides, I think you're comparing apples to oranges. In the SOL example, you use the same declaration for all types. Shouldn't the SOL example be: val a = (1,2) : [Int,Int] val b = (1,2) : [Int,Int] val c = (2,3) : MyCustomTupleType[Int,Int] which would probably generate: assert(a == b); // ok assert(a != c); // Error: incompatible types for ((a) != (b))
Oct 20 2009
parent reply language_fan <foo bar.com.invalid> writes:
Tue, 20 Oct 2009 16:25:05 -0400, Robert Jacques thusly wrote:

 On Tue, 20 Oct 2009 15:19:15 -0400, language_fan <foo bar.com.invalid>
 wrote:
 
 Real tuple types do not have a special type tag which gets injected
 implicitly with structs. So every time you try to do something
 lightweight by emulating tuples, you need to refer to the global Tuple
 type or bang your head to the wall.
Or use a templated opAssign mixin to allow two desperate types to be assigned to each other.
Wow, you need templates to implement == for built-in values types, nice..
 Besides, I think you're comparing apples to oranges. In the SOL example,
 you use the same declaration for all types. Shouldn't the SOL example
 be:
 
    val a = (1,2) : [Int,Int]
    val b = (1,2) : [Int,Int]
    val c = (2,3) : MyCustomTupleType[Int,Int]
 
 which would probably generate:
       assert(a == b); // ok
       assert(a != c); // Error: incompatible types for ((a) != (b))
If you have built-in tuple literals, there is no way you can build a MyCustomTupleType without resorting to other language features. There are no apples and oranges, cause they both are seen as (Int,Int) by the equivalence checker. Do you understand how equivalence works in structural typing system (http://en.wikipedia.org/wiki/ Structural_type_system) vs nominal typing? In structural equivalence there are no names attached to the types (well there might be, but those are omitted in the comparison), only their internal structure matters. Why would anyone want to create two incompatible tuples by default as you still would have 'typedef' and 'struct' for implementing just that.
Oct 20 2009
parent reply "Robert Jacques" <sandford jhu.edu> writes:
On Wed, 21 Oct 2009 02:23:09 -0400, language_fan <foo bar.com.invalid>  
wrote:
 Tue, 20 Oct 2009 16:25:05 -0400, Robert Jacques thusly wrote:

 On Tue, 20 Oct 2009 15:19:15 -0400, language_fan <foo bar.com.invalid>
 wrote:

 Real tuple types do not have a special type tag which gets injected
 implicitly with structs. So every time you try to do something
 lightweight by emulating tuples, you need to refer to the global Tuple
 type or bang your head to the wall.
Or use a templated opAssign mixin to allow two desperate types to be assigned to each other.
Wow, you need templates to implement == for built-in values types, nice..
Unlike C++, D templates don't require a PhD to use. And it's definitely better that banging your head against the wall.
 Besides, I think you're comparing apples to oranges. In the SOL example,
 you use the same declaration for all types. Shouldn't the SOL example
 be:

    val a = (1,2) : [Int,Int]
    val b = (1,2) : [Int,Int]
    val c = (2,3) : MyCustomTupleType[Int,Int]

 which would probably generate:
       assert(a == b); // ok
       assert(a != c); // Error: incompatible types for ((a) != (b))
If you have built-in tuple literals, there is no way you can build a MyCustomTupleType without resorting to other language features. There are no apples and oranges, cause they both are seen as (Int,Int) by the equivalence checker. Do you understand how equivalence works in structural typing system (http://en.wikipedia.org/wiki/ Structural_type_system) vs nominal typing? In structural equivalence there are no names attached to the types (well there might be, but those are omitted in the comparison), only their internal structure matters. Why would anyone want to create two incompatible tuples by default as you still would have 'typedef' and 'struct' for implementing just that.
My issue was that all your example _showed_ was nominal typing. Though I didn't mention it by name, I did mention that if SOL tuples had structural typing, it might would be a different story. (Well, until/if opImplicitCast was implemented, as it would allow for structural typing.)
Oct 21 2009
parent reply language_fan <foo bar.com.invalid> writes:
Wed, 21 Oct 2009 11:07:29 -0400, Robert Jacques thusly wrote:

 On Wed, 21 Oct 2009 02:23:09 -0400, language_fan <foo bar.com.invalid>
 wrote:
 Tue, 20 Oct 2009 16:25:05 -0400, Robert Jacques thusly wrote:

 On Tue, 20 Oct 2009 15:19:15 -0400, language_fan <foo bar.com.invalid>
 wrote:

 Real tuple types do not have a special type tag which gets injected
 implicitly with structs. So every time you try to do something
 lightweight by emulating tuples, you need to refer to the global
 Tuple type or bang your head to the wall.
Or use a templated opAssign mixin to allow two desperate types to be assigned to each other.
Wow, you need templates to implement == for built-in values types, nice..
Unlike C++, D templates don't require a PhD to use. And it's definitely better that banging your head against the wall.
 Besides, I think you're comparing apples to oranges. In the SOL
 example, you use the same declaration for all types. Shouldn't the SOL
 example be:

    val a = (1,2) : [Int,Int]
    val b = (1,2) : [Int,Int]
    val c = (2,3) : MyCustomTupleType[Int,Int]

 which would probably generate:
       assert(a == b); // ok
       assert(a != c); // Error: incompatible types for ((a) != (b))
If you have built-in tuple literals, there is no way you can build a MyCustomTupleType without resorting to other language features. There are no apples and oranges, cause they both are seen as (Int,Int) by the equivalence checker. Do you understand how equivalence works in structural typing system (http://en.wikipedia.org/wiki/ Structural_type_system) vs nominal typing? In structural equivalence there are no names attached to the types (well there might be, but those are omitted in the comparison), only their internal structure matters. Why would anyone want to create two incompatible tuples by default as you still would have 'typedef' and 'struct' for implementing just that.
My issue was that all your example _showed_ was nominal typing. Though I didn't mention it by name, I did mention that if SOL tuples had structural typing, it might would be a different story. (Well, until/if opImplicitCast was implemented, as it would allow for structural typing.)
Why do you insist on using nominal typing for tuples and the library defined "literal". If I want plain old tuples without any kind of type name, why should I care about extra hand waving needed to make it work. I see opImplicitCast, introspection done with templates and all kinds of other voodoo to be ugly hacks. This is nothing more than one of the basic value types without any special semantics, for $deity's sake. If you like the hacky approach so much, why don't all built-in types (like static arrays etc.) use the same method? Implementation wise tuples are much simpler than D's arrays or AAs, still they are treated like some 2nd class citizen from some notorious 3rd world country. Why is it so damn hard to change the 90% correctly implemented built-in tuples to work like in any other tuple supporting language. Do you somehow fancy verbose syntax for C++ compatibility reasons (ah, the good old std::tr1::make_tuple<int, int>, makes me want to wank every time..) Try Ruby, try python, try *ML etc. They all somehow got it right.
Oct 21 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
language_fan wrote:
 Wed, 21 Oct 2009 11:07:29 -0400, Robert Jacques thusly wrote:
 My issue was that all your example _showed_ was nominal typing. Though I
 didn't mention it by name, I did mention that if SOL tuples had
 structural typing, it might would be a different story. (Well, until/if
 opImplicitCast was implemented, as it would allow for structural
 typing.)
Why do you insist on using nominal typing for tuples and the library defined "literal". If I want plain old tuples without any kind of type name, why should I care about extra hand waving needed to make it work.
I'm late in this dialog, but I'm not seeing an impediment here. What does it matter to you that tuples actually have a name vs. not having a name at all?
 I 
 see opImplicitCast, introspection done with templates and all kinds of 
 other voodoo to be ugly hacks. This is nothing more than one of the basic 
 value types without any special semantics, for $deity's sake. If you like 
 the hacky approach so much, why don't all built-in types (like static 
 arrays etc.) use the same method? Implementation wise tuples are much 
 simpler than D's arrays or AAs, still they are treated like some 2nd 
 class citizen from some notorious 3rd world country.
How and why are they treated badly?
 Why is it so damn hard to change the 90% correctly implemented built-in 
 tuples to work like in any other tuple supporting language. Do you 
 somehow fancy verbose syntax for C++ compatibility reasons (ah, the good 
 old std::tr1::make_tuple<int, int>, makes me want to wank every time..) 
 Try Ruby, try python, try *ML etc. They all somehow got it right.
What exactly didn't D's tuples get right? Andrei
Oct 21 2009
next sibling parent reply Bill Baxter <wbaxter gmail.com> writes:
On Wed, Oct 21, 2009 at 8:48 AM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 language_fan wrote:
 Wed, 21 Oct 2009 11:07:29 -0400, Robert Jacques thusly wrote:
 My issue was that all your example _showed_ was nominal typing. Though I
 didn't mention it by name, I did mention that if SOL tuples had
 structural typing, it might would be a different story. (Well, until/if
 opImplicitCast was implemented, as it would allow for structural
 typing.)
Why do you insist on using nominal typing for tuples and the library defined "literal". If I want plain old tuples without any kind of type name, why should I care about extra hand waving needed to make it work.
I'm late in this dialog, but I'm not seeing an impediment here. What does it matter to you that tuples actually have a name vs. not having a name at all?
 I see opImplicitCast, introspection done with templates and all kinds of
 other voodoo to be ugly hacks. This is nothing more than one of the basic
 value types without any special semantics, for $deity's sake. If you like
 the hacky approach so much, why don't all built-in types (like static arrays
 etc.) use the same method? Implementation wise tuples are much simpler than
 D's arrays or AAs, still they are treated like some 2nd class citizen from
 some notorious 3rd world country.
How and why are they treated badly?
 Why is it so damn hard to change the 90% correctly implemented built-in
 tuples to work like in any other tuple supporting language. Do you somehow
 fancy verbose syntax for C++ compatibility reasons (ah, the good old
 std::tr1::make_tuple<int, int>, makes me want to wank every time..) Try
 Ruby, try python, try *ML etc. They all somehow got it right.
What exactly didn't D's tuples get right?
Well, auto-flattening is a colossally bad idea to be sure. --bb
Oct 21 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 Well, auto-flattening is a colossally bad idea to be sure.
Well that I agree with. I wonder if we need to fix it for D2. If we don't, we risk to live with it like with a chronic cough. Andrei
Oct 21 2009
prev sibling parent reply language_fan <foo bar.com.invalid> writes:
Wed, 21 Oct 2009 10:48:34 -0500, Andrei Alexandrescu thusly wrote:

 language_fan wrote:
 Wed, 21 Oct 2009 11:07:29 -0400, Robert Jacques thusly wrote:
 My issue was that all your example _showed_ was nominal typing. Though
 I didn't mention it by name, I did mention that if SOL tuples had
 structural typing, it might would be a different story. (Well,
 until/if opImplicitCast was implemented, as it would allow for
 structural typing.)
Why do you insist on using nominal typing for tuples and the library defined "literal". If I want plain old tuples without any kind of type name, why should I care about extra hand waving needed to make it work.
I'm late in this dialog, but I'm not seeing an impediment here. What does it matter to you that tuples actually have a name vs. not having a name at all?
Using tuples in D is a major pain in the ass. In fact it has been made so hard that people start avoiding the feature like plague. I wrote some test code to reveal how inconsistent their semantics are. Note that you need the built-in tuples for some stuff, like assigning and mixed value/ type tuples. Stuples can only be used as values and when the auto- flattening is not desired.
 STARTS HERE
template Tuple(T...) { alias T Tuple; } struct STuple(T...) { T t; } void main() { Tuple!(int,int) a; // typeof(this) *is* the official tuple type STuple!(int,int) b; // this is actually a struct a = Tuple!(1,1); // ok // Tuple!(int,int) a2 = Tuple!(1,1); // WTF? Error: cannot implicitly convert expression (tuple(1,1)) of type (int, int) to int auto a3 = Tuple!(1,1); // ok b = STuple!(int,int)(1,1); // no easier way? make_tuple!(1,1) ? STuple!(int,int) b2 = STuple!(int,int)(1,1); // ok, but very verbose auto e1 = a[0]; // ok //auto e2 = b[0]; // nope auto e3 = b.t[0]; // this is how it works - you could possibly define opIndex but how would it work with different types then writefln("%s", a); // we get.. 1 !? but.. writefln("%s", typeof(a).stringof); // (int, int) writefln("%s", b); // STuple!(int,int)(1, 1) - rather verbose, but suffices //auto retTest() { return STuple!(int,int)(1,1); } // no identifier for declarator retTest STuple!(int,int) retTest2() { return STuple!(int,int)(1,1); }; // ok, but a bit too verbose int d,e; Tuple!(d,e) = Tuple!(10,20); // ok // but how to discard an unnecessary value? e.g. (a, _) = (1, 2) // Tuple!(d,e) = STuple!(10,20); // nope, not interchangeable // b = STuple!(a); // same here a = a; // ok b = b; // ok // a = b; // Error: a is not an lvalue -- interesting! Tuple!(d,b) = Tuple!(1, retTest2()); // awesome, with the Tuple I can even assign Tuples of STuples! //Tuple!(d,a) = Tuple!(1, a); // but Tuples don't help when assigning Tuples of Tuples //test.d(12): Error: expression _a_field_0 is not a valid template value argument //test.d(12): Error: expression _a_field_1 is not a valid template value argument //test.d(47): Error: template instance test.Tuple! (1,_a_field_0,_a_field_1) error instantiating // LET'S MAKE ARRAYS! int[] a1; // ok int[][] a2; // ok auto a1b = [1,2,3]; // ok auto a2b = [[1],[2],[3]]; // ok //Tuple!(int,int)[] a4; // Error: can't have array of (int, int) -- WHY NOT - it's simple, try e.g. ML STuple!(int,int)[] a5; // ok auto a3b = [Tuple!(1,1)]; // works, but hey did you know this is an array of ints! auto a4b = [STuple!(int,int)(1,1)]; // ok // int[Tuple!(int,int)] a6; // Error: can't have associative array key of (int, int) int[STuple!(int,int)] a7; // ok a7[STuple!(int,int)(1,1)] = 5; // ok Tuple!(int,Tuple!(int,int)) a8; // this isn't a (int, (int,int)) tuple - it's (int,int,int) ! STuple!(int,STuple!(int,int)) a9; // ok //auto a10 = [ Tuple!(1,1) : 2 ]; // Error: can't have associative array key of (int, int) -- Why did this work in array literal then?! auto a11 = [ STuple!(int,int)(1,1) : 2 ]; // ok alias Tuple!(int, 5) foo; // alias STuple!(int, 5) foo2; // parameters can only be types }
Oct 21 2009
next sibling parent language_fan <foo bar.com.invalid> writes:
Wed, 21 Oct 2009 16:54:22 +0000, language_fan thusly wrote:

 Wed, 21 Oct 2009 10:48:34 -0500, Andrei Alexandrescu thusly wrote:
 
 language_fan wrote:
 Wed, 21 Oct 2009 11:07:29 -0400, Robert Jacques thusly wrote:
 My issue was that all your example _showed_ was nominal typing.
 Though I didn't mention it by name, I did mention that if SOL tuples
 had structural typing, it might would be a different story. (Well,
 until/if opImplicitCast was implemented, as it would allow for
 structural typing.)
Why do you insist on using nominal typing for tuples and the library defined "literal". If I want plain old tuples without any kind of type name, why should I care about extra hand waving needed to make it work.
I'm late in this dialog, but I'm not seeing an impediment here. What does it matter to you that tuples actually have a name vs. not having a name at all?
Using tuples in D is a major pain in the ass. In fact it has been made so hard that people start avoiding the feature like plague. I wrote some test code to reveal how inconsistent their semantics are. Note that you need the built-in tuples for some stuff, like assigning and mixed value/ type tuples. Stuples can only be used as values and when the auto- flattening is not desired.
I forgot to say that you need two kinds of tuple "literals" in your standard library in D: one with value semantics, and one that is a wrapper around the alias tuple since auto-flattening happens.
Oct 21 2009
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
language_fan wrote:
 Wed, 21 Oct 2009 10:48:34 -0500, Andrei Alexandrescu thusly wrote:
 
 language_fan wrote:
 Wed, 21 Oct 2009 11:07:29 -0400, Robert Jacques thusly wrote:
 My issue was that all your example _showed_ was nominal typing. Though
 I didn't mention it by name, I did mention that if SOL tuples had
 structural typing, it might would be a different story. (Well,
 until/if opImplicitCast was implemented, as it would allow for
 structural typing.)
Why do you insist on using nominal typing for tuples and the library defined "literal". If I want plain old tuples without any kind of type name, why should I care about extra hand waving needed to make it work.
I'm late in this dialog, but I'm not seeing an impediment here. What does it matter to you that tuples actually have a name vs. not having a name at all?
Using tuples in D is a major pain in the ass. In fact it has been made so hard that people start avoiding the feature like plague.
How do you know what "people" do?
 I wrote some 
 test code to reveal how inconsistent their semantics are. Note that you 
 need the built-in tuples for some stuff, like assigning and mixed value/
 type tuples. Stuples can only be used as values and when the auto-
 flattening is not desired.
 
 STARTS HERE
template Tuple(T...) { alias T Tuple; }
Why do you define Tuple instead of using the standard std.typecons.tuple?
 struct STuple(T...) {
   T t;
 }
Why do you insist on defining another tuple type instead of using the one provided by the standard library?
 void main() {
   Tuple!(int,int) a; // typeof(this) *is* the official tuple type
   STuple!(int,int) b;  // this is actually a struct
 
   a = Tuple!(1,1);  // ok
That doesn't work for me at all (with std.typecons.Tuple). I think there is confusion about a couple of things. One is that Tuple!(int, int) is a type that contains two ints, whereas Tuple!(1, 1) is a type that contains two compile-time integral values. So if you write: a = Tuple!(1, 1); that is as good syntactically as: a = int; which I hope you agree shouldn't quite go through. Write this: Tuple!(int,int) a; a = tuple(1, 1); or this: auto a = tuple(1, 1);
   // Tuple!(int,int) a2 = Tuple!(1,1); // WTF? Error: cannot implicitly 
 convert expression (tuple(1,1)) of type (int, int) to int
Yeah, WTF that doesn't work either: int a2 = int;
   auto a3 = Tuple!(1,1); // ok
Not ok on my machine, nor it should be ok as this is also not ok: auto a3 = int;
   b = STuple!(int,int)(1,1); // no easier way? make_tuple!(1,1) ?
Yeah try tuple(1, 1) in conjunction with std.typecons.Tuple.
   STuple!(int,int) b2 = STuple!(int,int)(1,1); // ok, but very verbose
Well write this: auto stuple(T...)(T args) { return STuple!(T)(args); }
   auto e1 = a[0];  // ok
This doesn't work because of a bug in the compiler, but this does with std.typecons.Tuple: auto e1 = a.field[0]; etc. etc. etc. I'm sure you make a couple of good points, but they are difficult to find. I suggest you peruse std.typecons.Tuple and submit any bugs you find to bugzilla. Andrei
Oct 21 2009
parent reply language_fan <foo bar.com.invalid> writes:
Wed, 21 Oct 2009 12:35:35 -0500, Andrei Alexandrescu thusly wrote:

 language_fan wrote:
 void main() {
   Tuple!(int,int) a; // typeof(this) *is* the official tuple type
   STuple!(int,int) b;  // this is actually a struct
 
   a = Tuple!(1,1);  // ok
That doesn't work for me at all (with std.typecons.Tuple). I think there is confusion about a couple of things. One is that Tuple!(int, int) is a type that contains two ints, whereas Tuple!(1, 1) is a type that contains two compile-time integral values. So if you write: a = Tuple!(1, 1); that is as good syntactically as: a = int;
It might be if we use your definition of tuple. But the mighty compiler dmd 2.035 himself calls my types constructed with my template
 template Tuple(T...) { alias T Tuple; }
a tuple. How do you explain that? If the resulting type is tuple according to dmd, why do you think it is actually not. If it works like you say, why does this work then?
  Tuple!(int,int) a;
  a = Tuple!(12,13);
Here 'a' has a real runtime type (int,int) which is a runtime tuple according to dmd. And I assign a value to the tuple on runtime. I can even test it by printing the values a[0] and a[1] with writefln. So why didn't you just fix this particular tuple type and why did you come up with a library level hack? Show me a better way to achieve this with your tuple system.
  int d,e;
  Tuple!(d,e) = Tuple!(10,20);
It's still not clear to me why you don't want to add full syntactic support for built-in tuples. Do you somehow find this kind of code difficult to read or maintain? (int,int) a = (1,1); int e1 = a[0]; writefln(a); (int,int) retTest() { return (1,1); } int d,e; (d,e) = (10,20); (d,_) = (10,20); (int,int) b; (d,b) = (1, retTest()); auto a3b = [ (1,1), (2,2) ]; auto a7 = [ (1,1) : 5 ]; auto a8 = [ 5 : (1,1)]; I want to know what is the rationale behind not accepting this semantics that is so widely used in other languages (and I very well mean the languages that *have* built-in tuple types).
Oct 21 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
language_fan wrote:
 Wed, 21 Oct 2009 12:35:35 -0500, Andrei Alexandrescu thusly wrote:
 
 language_fan wrote:
 void main() {
   Tuple!(int,int) a; // typeof(this) *is* the official tuple type
   STuple!(int,int) b;  // this is actually a struct

   a = Tuple!(1,1);  // ok
That doesn't work for me at all (with std.typecons.Tuple). I think there is confusion about a couple of things. One is that Tuple!(int, int) is a type that contains two ints, whereas Tuple!(1, 1) is a type that contains two compile-time integral values. So if you write: a = Tuple!(1, 1); that is as good syntactically as: a = int;
It might be if we use your definition of tuple. But the mighty compiler dmd 2.035 himself calls my types constructed with my template
 template Tuple(T...) { alias T Tuple; }
a tuple. How do you explain that? If the resulting type is tuple according to dmd, why do you think it is actually not. If it works like you say, why does this work then?
  Tuple!(int,int) a;
  a = Tuple!(12,13);
Here 'a' has a real runtime type (int,int) which is a runtime tuple according to dmd. And I assign a value to the tuple on runtime. I can even test it by printing the values a[0] and a[1] with writefln. So why didn't you just fix this particular tuple type and why did you come up with a library level hack? Show me a better way to achieve this with your tuple system.
I don't understand what you are trying to accomplish. As far as I can tell you want to do this: Tuple!(int, int) a; a = tuple(12, 13); int x = a.field[0]; and similar things. I have no idea why you refuse to do it that way.
  int d,e;
  Tuple!(d,e) = Tuple!(10,20);
It's still not clear to me why you don't want to add full syntactic support for built-in tuples. Do you somehow find this kind of code difficult to read or maintain? (int,int) a = (1,1); int e1 = a[0]; writefln(a); (int,int) retTest() { return (1,1); } int d,e; (d,e) = (10,20); (d,_) = (10,20); (int,int) b; (d,b) = (1, retTest()); auto a3b = [ (1,1), (2,2) ]; auto a7 = [ (1,1) : 5 ]; auto a8 = [ 5 : (1,1)]; I want to know what is the rationale behind not accepting this semantics that is so widely used in other languages (and I very well mean the languages that *have* built-in tuple types).
To effect this, there'd first be a need to eliminate the current semantics of the comma operator. I probably find it as useless as the next guy, and don't take one second to buy into Walter's theory that it makes it easy to generate code (that may as well be his least convincing argument to date), but really there's two parts to your suggestion: (1) eliminate the comma operator, (2) make the comma operator work for tuples. I suspect there are some syntactical issues with (2). Andrei
Oct 21 2009
next sibling parent reply language_fan <foo bar.com.invalid> writes:
Wed, 21 Oct 2009 13:41:50 -0500, Andrei Alexandrescu thusly wrote:

 I don't understand what you are trying to accomplish. As far as I can
 tell you want to do this:
 
 Tuple!(int, int) a;
 a = tuple(12, 13);
 int x = a.field[0];
Not only that, but also this:
  int d,e;
  Tuple!(d,e) = Tuple!(10,20);
 and similar things. I have no idea why you refuse to do it that way.
You somehow refuse to see that D has a tuple type which the compiler calls a tuple. Run 'strings /path/to/dmd|wc -l' and you'll see 28 instances of the word 'tuple'. If there was no built-in support, why does the executable contain the word then? Here..
 template Z(T...) { alias T Z; }
 Z!(int,int) a;
 pragma(msg,a.stringof);
I did not mention the word tuple, but guess what dmd thinks. If it is not a tuple, why does dmd output 'tuple(_a_field_0,_a_field_1)'? Apparently this does not follow the math book definition of tuple nor is the traditional FPL tuple, but a special D flavor of tuple. Walter decided to build a tuple type, but something happened and he later started restricting its use (IIRC it *was* possible to create an array of these tuples, but now it is disallowed). What is left is a half-working mess mostly useful for compile time meta-programming. It works rather nicely there (except that the auto-flattening is sometimes rather annoying), but its operation is broken on runtime. You discarded this by stating that D does not have a tuple type, you just call it a 'type that contains two ints' in my example. DMD calls it a tuple (see .stringof, error messages etc.) I can see that you try to accomplish the same things with your library provided version of tuple. But if that is the recommended way of using tuples, why there is a bug riddled version in the compiler of the same type. It is extremely exhausting to implement a new compiler for D because the reference implementation is full of bugs I mentioned in previous posts and you cannot really tell how it should work.
 To effect this, there'd first be a need to eliminate the current
 semantics of the comma operator. I probably find it as useless as the
 next guy, and don't take one second to buy into Walter's theory that it
 makes it easy to generate code (that may as well be his least convincing
 argument to date), but really there's two parts to your suggestion: (1)
 eliminate the comma operator, (2) make the comma operator work for
 tuples. I suspect there are some syntactical issues with (2).
I know that very well..
Oct 21 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
language_fan wrote:
 You somehow refuse to see that D has a tuple type which the compiler 
 calls a tuple.
D has tuples, it doesn't have tuple literals. Tuple attempts to supplant that. Andrei
Oct 21 2009
prev sibling parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Wed, 21 Oct 2009 22:41:50 +0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 language_fan wrote:
 Wed, 21 Oct 2009 12:35:35 -0500, Andrei Alexandrescu thusly wrote:

 language_fan wrote:
 void main() {
   Tuple!(int,int) a; // typeof(this) *is* the official tuple type
   STuple!(int,int) b;  // this is actually a struct

   a = Tuple!(1,1);  // ok
That doesn't work for me at all (with std.typecons.Tuple). I think there is confusion about a couple of things. One is that Tuple!(int, int) is a type that contains two ints, whereas Tuple!(1, 1) is a type that contains two compile-time integral values. So if you write: a = Tuple!(1, 1); that is as good syntactically as: a = int;
It might be if we use your definition of tuple. But the mighty compiler dmd 2.035 himself calls my types constructed with my template
 template Tuple(T...) { alias T Tuple; }
a tuple. How do you explain that? If the resulting type is tuple according to dmd, why do you think it is actually not. If it works like you say, why does this work then?
  Tuple!(int,int) a;
  a = Tuple!(12,13);
Here 'a' has a real runtime type (int,int) which is a runtime tuple according to dmd. And I assign a value to the tuple on runtime. I can even test it by printing the values a[0] and a[1] with writefln. So why didn't you just fix this particular tuple type and why did you come up with a library level hack? Show me a better way to achieve this with your tuple system.
I don't understand what you are trying to accomplish. As far as I can tell you want to do this: Tuple!(int, int) a; a = tuple(12, 13); int x = a.field[0]; and similar things. I have no idea why you refuse to do it that way.
  int d,e;
  Tuple!(d,e) = Tuple!(10,20);
It's still not clear to me why you don't want to add full syntactic support for built-in tuples. Do you somehow find this kind of code difficult to read or maintain? (int,int) a = (1,1); int e1 = a[0]; writefln(a); (int,int) retTest() { return (1,1); } int d,e; (d,e) = (10,20); (d,_) = (10,20); (int,int) b; (d,b) = (1, retTest()); auto a3b = [ (1,1), (2,2) ]; auto a7 = [ (1,1) : 5 ]; auto a8 = [ 5 : (1,1)]; I want to know what is the rationale behind not accepting this semantics that is so widely used in other languages (and I very well mean the languages that *have* built-in tuple types).
To effect this, there'd first be a need to eliminate the current semantics of the comma operator. I probably find it as useless as the next guy, and don't take one second to buy into Walter's theory that it makes it easy to generate code (that may as well be his least convincing argument to date), but really there's two parts to your suggestion: (1) eliminate the comma operator, (2) make the comma operator work for tuples. I suspect there are some syntactical issues with (2). Andrei
Well, he could just change the symbol used to denote comma operator to some other character (if he uses it so heavily internally), and don't expose it to a user. I don't use tuples a lot myself, but I would love to have multiple return types without some clumsy syntax. It's possible even now, but the syntax is a bit discouraging: Tuple!(int,float) foo() { return tuple(42, -1.0f); } make_tuple(a, b) = foo(); as opposed to: (int, float) foo() { return (42, -1.0f); } (a, b) = foo();
Oct 21 2009
parent reply grauzone <none example.net> writes:
Denis Koroskin wrote:
 On Wed, 21 Oct 2009 22:41:50 +0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 
 language_fan wrote:
 Wed, 21 Oct 2009 12:35:35 -0500, Andrei Alexandrescu thusly wrote:

 language_fan wrote:
 void main() {
   Tuple!(int,int) a; // typeof(this) *is* the official tuple type
   STuple!(int,int) b;  // this is actually a struct

   a = Tuple!(1,1);  // ok
That doesn't work for me at all (with std.typecons.Tuple). I think there is confusion about a couple of things. One is that Tuple!(int, int) is a type that contains two ints, whereas Tuple!(1, 1) is a type that contains two compile-time integral values. So if you write: a = Tuple!(1, 1); that is as good syntactically as: a = int;
It might be if we use your definition of tuple. But the mighty compiler dmd 2.035 himself calls my types constructed with my template
 template Tuple(T...) { alias T Tuple; }
a tuple. How do you explain that? If the resulting type is tuple according to dmd, why do you think it is actually not. If it works like you say, why does this work then?
  Tuple!(int,int) a;
  a = Tuple!(12,13);
Here 'a' has a real runtime type (int,int) which is a runtime tuple according to dmd. And I assign a value to the tuple on runtime. I can even test it by printing the values a[0] and a[1] with writefln. So why didn't you just fix this particular tuple type and why did you come up with a library level hack? Show me a better way to achieve this with your tuple system.
I don't understand what you are trying to accomplish. As far as I can tell you want to do this: Tuple!(int, int) a; a = tuple(12, 13); int x = a.field[0]; and similar things. I have no idea why you refuse to do it that way.
  int d,e;
  Tuple!(d,e) = Tuple!(10,20);
It's still not clear to me why you don't want to add full syntactic support for built-in tuples. Do you somehow find this kind of code difficult to read or maintain? (int,int) a = (1,1); int e1 = a[0]; writefln(a); (int,int) retTest() { return (1,1); } int d,e; (d,e) = (10,20); (d,_) = (10,20); (int,int) b; (d,b) = (1, retTest()); auto a3b = [ (1,1), (2,2) ]; auto a7 = [ (1,1) : 5 ]; auto a8 = [ 5 : (1,1)]; I want to know what is the rationale behind not accepting this semantics that is so widely used in other languages (and I very well mean the languages that *have* built-in tuple types).
To effect this, there'd first be a need to eliminate the current semantics of the comma operator. I probably find it as useless as the next guy, and don't take one second to buy into Walter's theory that it makes it easy to generate code (that may as well be his least convincing argument to date), but really there's two parts to your suggestion: (1) eliminate the comma operator, (2) make the comma operator work for tuples. I suspect there are some syntactical issues with (2). Andrei
Well, he could just change the symbol used to denote comma operator to some other character (if he uses it so heavily internally), and don't expose it to a user. I don't use tuples a lot myself, but I would love to have multiple return types without some clumsy syntax. It's possible even now, but the syntax is a bit discouraging: Tuple!(int,float) foo() { return tuple(42, -1.0f); } make_tuple(a, b) = foo(); as opposed to: (int, float) foo() { return (42, -1.0f); } (a, b) = foo();
Or even: a, b = foo(); or a, _ = foo(); Works in Python (tm)
Oct 21 2009
parent reply Leandro Lucarella <llucax gmail.com> writes:
grauzone, el 21 de octubre a las 22:12 me escribiste:
 Or even:
 
 a, b = foo();
 
 or
 
 a, _ = foo();
 
 Works in Python (tm)
_ is a regular symbol (variable name in this case), there is nothing special about the second form, which is exactly the same as the first. And BTW, the trailing ; is not needed ;) -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- Hey you, standing in the aisles With itchy feet and fading smiles Can you feel me?
Oct 21 2009
parent reply grauzone <none example.net> writes:
Leandro Lucarella wrote:
 grauzone, el 21 de octubre a las 22:12 me escribiste:
 Or even:

 a, b = foo();

 or

 a, _ = foo();

 Works in Python (tm)
_ is a regular symbol (variable name in this case), there is nothing special about the second form, which is exactly the same as the first. And
I didn't even know this. In Python, it doesn't matter anyway, because it is dynamically typed, and you can assign arbitrary values to the same variable. Apparently, writing foreach (index,_;something) in D isn't a special case either; it just declares a variable named _. Several nested loops using _ are not possible. PS: let's hope Walter recognizes the need of *good* syntax for a simple, basic language construct like tuples. Just look at language_fan's D code and compare it with the corresponding Python code posted afterward. The D code looks so ridiculous.
 BTW, the trailing ; is not needed ;)
 
Oct 21 2009
parent Leandro Lucarella <llucax gmail.com> writes:
grauzone, el 21 de octubre a las 23:32 me escribiste:
 Leandro Lucarella wrote:
grauzone, el 21 de octubre a las 22:12 me escribiste:
Or even:

a, b = foo();

or

a, _ = foo();

Works in Python (tm)
_ is a regular symbol (variable name in this case), there is nothing special about the second form, which is exactly the same as the first. And
I didn't even know this. In Python, it doesn't matter anyway, because it is dynamically typed, and you can assign arbitrary values to the same variable. Apparently, writing foreach (index,_;something) in D isn't a special case either; it just declares a variable named _. Several nested loops using _ are not possible.
Using _ is a bad idea if you use gettext though ;) -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- El techo de mi cuarto lleno de estrellas
Oct 21 2009
prev sibling parent reply language_fan <foo bar.com.invalid> writes:
Wed, 21 Oct 2009 16:54:22 +0000, language_fan thusly wrote:

 Wed, 21 Oct 2009 10:48:34 -0500, Andrei Alexandrescu thusly wrote:
 
 language_fan wrote:
 Wed, 21 Oct 2009 11:07:29 -0400, Robert Jacques thusly wrote:
 My issue was that all your example _showed_ was nominal typing.
 Though I didn't mention it by name, I did mention that if SOL tuples
 had structural typing, it might would be a different story. (Well,
 until/if opImplicitCast was implemented, as it would allow for
 structural typing.)
Why do you insist on using nominal typing for tuples and the library defined "literal". If I want plain old tuples without any kind of type name, why should I care about extra hand waving needed to make it work.
I'm late in this dialog, but I'm not seeing an impediment here. What does it matter to you that tuples actually have a name vs. not having a name at all?
Using tuples in D is a major pain in the ass. In fact it has been made so hard that people start avoiding the feature like plague. I wrote some test code to reveal how inconsistent their semantics are. Note that you need the built-in tuples for some stuff, like assigning and mixed value/ type tuples. Stuples can only be used as values and when the auto- flattening is not desired.
 STARTS HERE
Even though not being an expert in Python (have not really used it), as an experiment I managed to successfully port the examples in 2 minutes without any kind of problems. a = (1,1) e1 = a[0] print a def retTest(): return (1,1) (d,e) = (10,20) (d,_) = (10,20) (d,b) = (1, retTest()) a3b = [ (1,1), (2,2) ] a7 = { (1,1) : 5 } a8 = { 5 : (1,1)}
Oct 21 2009
parent reply Leandro Lucarella <llucax gmail.com> writes:
language_fan, el 21 de octubre a las 17:52 me escribiste:
 Wed, 21 Oct 2009 16:54:22 +0000, language_fan thusly wrote:
 
 Wed, 21 Oct 2009 10:48:34 -0500, Andrei Alexandrescu thusly wrote:
 
 language_fan wrote:
 Wed, 21 Oct 2009 11:07:29 -0400, Robert Jacques thusly wrote:
 My issue was that all your example _showed_ was nominal typing.
 Though I didn't mention it by name, I did mention that if SOL tuples
 had structural typing, it might would be a different story. (Well,
 until/if opImplicitCast was implemented, as it would allow for
 structural typing.)
Why do you insist on using nominal typing for tuples and the library defined "literal". If I want plain old tuples without any kind of type name, why should I care about extra hand waving needed to make it work.
I'm late in this dialog, but I'm not seeing an impediment here. What does it matter to you that tuples actually have a name vs. not having a name at all?
Using tuples in D is a major pain in the ass. In fact it has been made so hard that people start avoiding the feature like plague. I wrote some test code to reveal how inconsistent their semantics are. Note that you need the built-in tuples for some stuff, like assigning and mixed value/ type tuples. Stuples can only be used as values and when the auto- flattening is not desired.
 STARTS HERE
Even though not being an expert in Python (have not really used it), as an experiment I managed to successfully port the examples in 2 minutes without any kind of problems.
I think this is an important point too. Maybe tuples are not *that* broken as stated in this thread, but they are quite more complex to understand than tuples in other languages, and they feel somehow hackish.
 a = (1,1)
 e1 = a[0]
 
 print a
 
 def retTest():
   return (1,1)
 
 (d,e) = (10,20)
 
 (d,_) = (10,20)
 
 (d,b) = (1, retTest())
 
 a3b = [ (1,1), (2,2) ]
 
 a7 = { (1,1) : 5 }
 
 a8 = { 5 : (1,1)}
In python you can even do: def f(a, b, c): print a, b, c x = (b, c) I think this is only possible in D with type tuples, not with ... "value tuples"? That's part of the complexity, having 2 type of tuples is very confusing. -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- You are the very reason why everything happens to you
Oct 21 2009
parent bearophile <bearophileHUGS lycos.com> writes:
Leandro Lucarella:

 In python you can even do:
 
 def f(a, b, c):
 	print a, b, c
 
 x = (b, c)

In Python2.x you can also do: def foo(x, (y, (z1, z2))): print z2 print z1 print y print x Z = (1, 2) yz = [0, Z] foo(-1, yz) That outputs: 2 1 0 -1 Bye, bearophile
Oct 21 2009
prev sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
language_fan, el 20 de octubre a las 19:19 me escribiste:
 One nasty thing about D's structs is that they don't have structural
 equivalence relation unlike tuples. So you need to use the same
 container struct type to get the same semantics. To achieve that you
 would need some kind of STuple on standard library level or other kinds
 of hacks.
 
 What I find unfortunate in D is that your abstractions come in two
 sizes - either you use the modest tiny construct that does not scale
 elegantly or the enormous hammer to crush things down theatretically.
I don't understand very well what are you saying anyways...
Because of the unnecessary nominal typing in D's tuple emulation, redefinitions of Tuples do not have implicit equivalence relation: struct Tuple(T...) { T t; } struct Tuple2(T...) { T t; } void main() { Tuple!(int,int) a; Tuple!(int,int) b; Tuple2!(int,int) c; assert(a == b); // ok assert(a != c); // Error: incompatible types for ((a) != (b)) } In some other language: val a = (1,2) : [Int,Int] val b = (1,2) : [Int,Int] val c = (2,3) : [Int,Int] assert(a == b); // ok assert(a != c); // ok Did you get it now?
Yes, thanks for the clarification.
 Real tuple types do not have a special type tag which gets injected 
 implicitly with structs. So every time you try to do something 
 lightweight by emulating tuples, you need to refer to the global Tuple 
 type or bang your head to the wall.
Yes, D support for tuples is way far from ideal. -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- If you want to be alone, just be alone If you want to watch the sea, just watch the sea But do it now, timing is the answer, do it now Timing is the answer to success
Oct 20 2009
parent reply "Robert Jacques" <sandford jhu.edu> writes:
On Tue, 20 Oct 2009 20:38:33 -0400, Leandro Lucarella <llucax gmail.com>  
wrote:
 language_fan, el 20 de octubre a las 19:19 me escribiste:
 One nasty thing about D's structs is that they don't have structural
 equivalence relation unlike tuples. So you need to use the same
 container struct type to get the same semantics. To achieve that you
 would need some kind of STuple on standard library level or other  
kinds
 of hacks.

 What I find unfortunate in D is that your abstractions come in two
 sizes - either you use the modest tiny construct that does not scale
 elegantly or the enormous hammer to crush things down theatretically.
I don't understand very well what are you saying anyways...
Because of the unnecessary nominal typing in D's tuple emulation, redefinitions of Tuples do not have implicit equivalence relation: struct Tuple(T...) { T t; } struct Tuple2(T...) { T t; } void main() { Tuple!(int,int) a; Tuple!(int,int) b; Tuple2!(int,int) c; assert(a == b); // ok assert(a != c); // Error: incompatible types for ((a) != (b)) } In some other language: val a = (1,2) : [Int,Int] val b = (1,2) : [Int,Int] val c = (2,3) : [Int,Int] assert(a == b); // ok assert(a != c); // ok Did you get it now?
Yes, thanks for the clarification.
 Real tuple types do not have a special type tag which gets injected
 implicitly with structs. So every time you try to do something
 lightweight by emulating tuples, you need to refer to the global Tuple
 type or bang your head to the wall.
Yes, D support for tuples is way far from ideal.
How so? I think this is merely the difference between a library type in a flexible language and a built-in type in an inflexible language. I mean the example was essentially: In D: Apple a Apple b Orange c assert(a != c); // Error: incompatible types Apple and Orange In SOL: Apple a Apple b Apple c assert(a != c); // ok, both a and c are apples. Now, if SOL allowed tuples to do things you can't do today in D, like assign a tuple to a struct with the same signature, then this might be a point. But that wasn't the example given. Now, the example was a good argument for making it easier and more natural to use the built-in tuple type. Adding syntaxtic sugar for tuples has been recommended before. I prefer using the slice syntax '..', as it would allow clean multi-dimensional slicing and mixed indexing and slicing, both of which are important to supporting arrays.
Oct 20 2009
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Robert Jacques wrote:
 On Tue, 20 Oct 2009 20:38:33 -0400, Leandro Lucarella <llucax gmail.com> 
 wrote:
 Yes, D support for tuples is way far from ideal.
How so? I think this is merely the difference between a library type in a flexible language and a built-in type in an inflexible language. I mean the example was essentially: In D: Apple a Apple b Orange c assert(a != c); // Error: incompatible types Apple and Orange In SOL: Apple a Apple b Apple c assert(a != c); // ok, both a and c are apples. Now, if SOL allowed tuples to do things you can't do today in D, like assign a tuple to a struct with the same signature, then this might be a point. But that wasn't the example given.
I also don't understand all the argument about structural vs. name equivalence. Andrei
Oct 20 2009
parent "Robert Jacques" <sandford jhu.edu> writes:
On Tue, 20 Oct 2009 22:45:53 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:
 Robert Jacques wrote:
 On Tue, 20 Oct 2009 20:38:33 -0400, Leandro Lucarella  
 <llucax gmail.com> wrote:
 Yes, D support for tuples is way far from ideal.
How so? I think this is merely the difference between a library type in a flexible language and a built-in type in an inflexible language. I mean the example was essentially: In D: Apple a Apple b Orange c assert(a != c); // Error: incompatible types Apple and Orange In SOL: Apple a Apple b Apple c assert(a != c); // ok, both a and c are apples. Now, if SOL allowed tuples to do things you can't do today in D, like assign a tuple to a struct with the same signature, then this might be a point. But that wasn't the example given.
I also don't understand all the argument about structural vs. name equivalence. Andrei
The original thread stated that D's value tuples (as opposed to type tuples) were far from ideal, because it's not a built-in type. So two people could make value tuple structs types that were incompatible with each other. (One counter to this is it's simple to define a templated opAssgin method that works correctly. Another counter is to relate this problem to typedefs). My issue was with the example comparing D to some-other-language (SOL). The issue was that only the built-in value-tuple type in SOL was shown, and not a value-tuple interfacing with something else that wasn't the built-in value-tuple. This indicates that SOL isn't flexible/expressive enough to have library value-tuple-types, or the problems with D's value-tuple type solution.
Oct 20 2009
prev sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
Robert Jacques, el 20 de octubre a las 21:06 me escribiste:
Real tuple types do not have a special type tag which gets injected
implicitly with structs. So every time you try to do something
lightweight by emulating tuples, you need to refer to the global Tuple
type or bang your head to the wall.
Yes, D support for tuples is way far from ideal.
How so? I think this is merely the difference between a library type in a flexible language and a built-in type in an inflexible language. I mean the example was essentially: In D: Apple a Apple b Orange c assert(a != c); // Error: incompatible types Apple and Orange In SOL: Apple a Apple b Apple c assert(a != c); // ok, both a and c are apples.
I wasn't referring to this particular example, even when I agree this is not a big issue, is much more difficult to end up comparing Apples to Oranges if the language have support for tuple literals (like in the example). In D I think you might find yourself in this situation more often, but still rarely. I think tuple literals is an important thing to encourage people using tuples, specially when you want to support functional programming style.
 Now, if SOL allowed tuples to do things you can't do today in D,
 like assign a tuple to a struct with the same signature, then this
 might be a point. But that wasn't the example given.
Yes, that's another thing that can be done without real tuple support in the language. Anyway, I guess I was a little exaggerated with '*way far* from ideal', but I'm convinced there is plenty of room for improvements. =) -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- FINALMENTE EL CABALLITO FABIAN VA A PASAR UNA BUENA NAVIDAD -- Crónica TV
Oct 20 2009
parent reply "Robert Jacques" <sandford jhu.edu> writes:
On Tue, 20 Oct 2009 23:30:48 -0400, Leandro Lucarella <llucax gmail.com>  
wrote:
 Robert Jacques, el 20 de octubre a las 21:06 me escribiste:
 Now, if SOL allowed tuples to do things you can't do today in D,
 like assign a tuple to a struct with the same signature, then this
 might be a point. But that wasn't the example given.
Yes, that's another thing that can be done without real tuple support in the language. Anyway, I guess I was a little exaggerated with '*way far* from ideal', but I'm convinced there is plenty of room for improvements. =)
Would you happen to know of a language which does tuples well already?
Oct 20 2009
parent reply Yigal Chripun <yigal100 gmail.com> writes:
On 21/10/2009 05:48, Robert Jacques wrote:
 On Tue, 20 Oct 2009 23:30:48 -0400, Leandro Lucarella <llucax gmail.com>
 wrote:
 Robert Jacques, el 20 de octubre a las 21:06 me escribiste:
 Now, if SOL allowed tuples to do things you can't do today in D,
 like assign a tuple to a struct with the same signature, then this
 might be a point. But that wasn't the example given.
Yes, that's another thing that can be done without real tuple support in the language. Anyway, I guess I was a little exaggerated with '*way far* from ideal', but I'm convinced there is plenty of room for improvements. =)
Would you happen to know of a language which does tuples well already?
pick any functional language.. my favorite is ML
Oct 20 2009
parent Leandro Lucarella <llucax gmail.com> writes:
Yigal Chripun, el 21 de octubre a las 07:18 me escribiste:
 On 21/10/2009 05:48, Robert Jacques wrote:
On Tue, 20 Oct 2009 23:30:48 -0400, Leandro Lucarella <llucax gmail.com>
wrote:
Robert Jacques, el 20 de octubre a las 21:06 me escribiste:
Now, if SOL allowed tuples to do things you can't do today in D,
like assign a tuple to a struct with the same signature, then this
might be a point. But that wasn't the example given.
Yes, that's another thing that can be done without real tuple support in the language. Anyway, I guess I was a little exaggerated with '*way far* from ideal', but I'm convinced there is plenty of room for improvements. =)
Would you happen to know of a language which does tuples well already?
pick any functional language.. my favorite is ML
Or Python ;) -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- HACIA NEUQUEN: EL JUEVES SALDRA CARAVANA CON PERROS DESDE CAPITAL EN APOYO AL CACHORRO CONDENADO A MUERTE -- Crónica TV
Oct 21 2009
prev sibling next sibling parent downs <default_357-line yahoo.de> writes:
Walter Bright wrote:
 Currently, static arrays are (as in C) half-value types and
 half-reference types. This tends to cause a series of weird problems and
 special cases in the language semantics, such as functions not being
 able to return static arrays, and out parameters not being possible to
 be static arrays.
 
 Andrei and I agonized over this for some time, and eventually came to
 the conclusion that static arrays should become value types. I.e.,
 
   T[3]
 
 should behave much as if it were:
 
   struct ??
   {
      T[3];
   }
 
 Then it can be returned from a function. In particular,
 
   void foo(T[3] a)
 
 is currently done (as in C) by passing a pointer to the array, and then
 with a bit of compiler magic 'a' is rewritten as (*a)[3]. Making this
 change would mean that the entire array would be pushed onto the
 parameter stack, i.e. a copy of the array, rather than a reference to it.
 
 Making this change would clean up the internal behavior of types.
 They'll be more orthogonal and consistent, and templates will work better.
 
 The previous behavior for function parameters can be retained by making
 it a ref parameter:
 
    void foo(ref T[3] a)
WOO! Thanks! :D
Oct 19 2009
prev sibling next sibling parent reply "Saaa" <empty needmail.com> writes:
Walter Bright wrote:
 Currently, static arrays are (as in C) half-value types and half-reference 
 types. This tends to cause a series of weird problems and special cases in 
 the language semantics, such as functions not being able to return static 
 arrays, and out parameters not being possible to be static arrays.

 Andrei and I agonized over this for some time, and eventually came to the 
 conclusion that static arrays should become value types. I.e.,

   T[3]

 should behave much as if it were:

   struct ??
   {
      T[3];
   }

 Then it can be returned from a function. In particular,

   void foo(T[3] a)

 is currently done (as in C) by passing a pointer to the array, and then 
 with a bit of compiler magic 'a' is rewritten as (*a)[3]. Making this 
 change would mean that the entire array would be pushed onto the parameter 
 stack, i.e. a copy of the array, rather than a reference to it.

 Making this change would clean up the internal behavior of types. They'll 
 be more orthogonal and consistent, and templates will work better.

 The previous behavior for function parameters can be retained by making it 
 a ref parameter:

    void foo(ref T[3] a)
Would that be D1&D2 or only D2?
Oct 19 2009
parent Walter Bright <newshound1 digitalmars.com> writes:
Saaa wrote:
 Would that be D1&D2 or only D2? 
D2 only, as it changes the behavior.
Oct 19 2009
prev sibling next sibling parent Chad J <chadjoan __spam.is.bad__gmail.com> writes:
Yes please!
Oct 19 2009
prev sibling next sibling parent Yigal Chripun <yigal100 gmail.com> writes:
On 20/10/2009 03:50, Walter Bright wrote:
 Currently, static arrays are (as in C) half-value types and
 half-reference types. This tends to cause a series of weird problems and
 special cases in the language semantics, such as functions not being
 able to return static arrays, and out parameters not being possible to
 be static arrays.

 Andrei and I agonized over this for some time, and eventually came to
 the conclusion that static arrays should become value types. I.e.,

 T[3]

 should behave much as if it were:

 struct ??
 {
 T[3];
 }

 Then it can be returned from a function. In particular,

 void foo(T[3] a)

 is currently done (as in C) by passing a pointer to the array, and then
 with a bit of compiler magic 'a' is rewritten as (*a)[3]. Making this
 change would mean that the entire array would be pushed onto the
 parameter stack, i.e. a copy of the array, rather than a reference to it.

 Making this change would clean up the internal behavior of types.
 They'll be more orthogonal and consistent, and templates will work better.

 The previous behavior for function parameters can be retained by making
 it a ref parameter:

 void foo(ref T[3] a)
that's good news :)
Oct 19 2009
prev sibling next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Walter Bright:

 The previous behavior for function parameters can be retained by making 
 it a ref parameter:
     void foo(ref T[3] a)
If I have generic code, like a templated function, that accepts both a dynamic and a static array, the function call will change its performance signature according to the type (if I don't add a "ref" the pass of a dynamic array will be O(1) while passing a fixed-size array will be O(n)). I can accept your idea (and I can see other people here seem to accept it), but I'd like the function to receive the array by value if the array is small, and by reference if it's large (small and large are defined in terms of true bytes). This can be done automatically by the compiler, but this looks unsafe, because it's bad when the compiler changes the program semantics in an invisible way. So something explicit is better: void foo(bigref T[3] a) That syntax means that if (T[3]).sizeof is big enough then it's a ref argument, otherwise it's a value argument. But I think it's better to be able to somehow define that "bigref" in the standard library: void foo(Bigref!T[3] a) Andrei may like something like that. To do that "ref" may need to change its nature a little, becoming a kind of subtype that the user can define and use. I am ignorant about this, maybe T[3].REF can be the type of the reference to T[3], as T[3]* is the type of the pointer to a T[3]. If something like this is possible then that Bigref!() becomes just a template that contains a static if that according to the value of (T[3]).sizeof and some constant threshold value becomes an alias of T[3] or T[3].REF. Questions: 1) How can I allocate a fixed-size array on the heap? 2) Can such array allocated on the heap be return by "ref" from a function? (Or do I have to return it just by pointer)? 3) How can I implement and use a variable-length struct like this, that sometimes improves the performance of some code? struct S(T) { T cargo; int len; int[0]; } (variable-length structs can even grow in both ways, and the pointer that points to them has to point to their middle, to a field that contains two lengths, but such structs are are less common, so they may be ignored in this discussion). 4) LLVM, the backend of LDC, can map small array operations on SSE operations, so it can often perform the sum among two fixed-sized arrays of integers in a single asm instruction ad clock cycle. I'd like D to pass such semantics to the backend. We can discuss this later. Bye, bearophile
Oct 19 2009
parent dsimcha <dsimcha yahoo.com> writes:
== Quote from bearophile (bearophileHUGS lycos.com)'s article
 Walter Bright:
 The previous behavior for function parameters can be retained by making
 it a ref parameter:
     void foo(ref T[3] a)
If I have generic code, like a templated function, that accepts both a dynamic
and a static array, the function call will change its performance signature according to the type (if I don't add a "ref" the pass of a dynamic array will be O(1) while passing a fixed-size array will be O(n)). Here's a way around that: To pass a static array by reference to a templated function that was written with generic ranges in mind, just slice it to make it a dynamic array: float[3] foo; pragma(msg, typeof(foo[]).stringof); // float[]
Oct 20 2009
prev sibling next sibling parent reply Don <nospam nospam.com> writes:
Walter Bright wrote:
 Currently, static arrays are (as in C) half-value types and 
 half-reference types. This tends to cause a series of weird problems and 
 special cases in the language semantics, such as functions not being 
 able to return static arrays, and out parameters not being possible to 
 be static arrays.
 
 Andrei and I agonized over this for some time, and eventually came to 
 the conclusion that static arrays should become value types. I.e.,
 
   T[3]
 
 should behave much as if it were:
 
   struct ??
   {
      T[3];
   }
 
 Then it can be returned from a function. In particular,
 
   void foo(T[3] a)
 
 is currently done (as in C) by passing a pointer to the array, and then 
 with a bit of compiler magic 'a' is rewritten as (*a)[3]. Making this 
 change would mean that the entire array would be pushed onto the 
 parameter stack, i.e. a copy of the array, rather than a reference to it.
 
 Making this change would clean up the internal behavior of types. 
 They'll be more orthogonal and consistent, and templates will work better.
 
 The previous behavior for function parameters can be retained by making 
 it a ref parameter:
 
    void foo(ref T[3] a)
I think this change is mandatory. We need it for SIMD operations. It will allow us to implement efficient vectors.
Oct 20 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
Don:

I think this change is mandatory. We need it for SIMD operations.<
Why? Why the compiler can't optimize things and perform SIMD operations with the fixed-sized array semantics of D1? (I ask this for LDC too, that's mostly D1 still). Bye, bearophile
Oct 20 2009
parent Don <nospam nospam.com> writes:
bearophile wrote:
 Don:
 
 I think this change is mandatory. We need it for SIMD operations.<
Why? Why the compiler can't optimize things and perform SIMD operations with the fixed-sized array semantics of D1? (I ask this for LDC too, that's mostly D1 still). Bye, bearophile
Because they are passed by reference. It certainly can't do it on D1: float dot(float[4] x) { x[3] = 4.5; // surprise! return 0; } void main() { float[4] a; a[3]= 0; float x = dot(a); assert(a[3]==0); // FAILS! }
Oct 20 2009
prev sibling next sibling parent Ary Borenszweig <ary esperanto.org.ar> writes:
Walter Bright wrote:
 Currently, static arrays are (as in C) half-value types and 
 half-reference types. This tends to cause a series of weird problems and 
 special cases in the language semantics, such as functions not being 
 able to return static arrays, and out parameters not being possible to 
 be static arrays.
 
 Andrei and I agonized over this for some time, and eventually came to 
 the conclusion that static arrays should become value types. I.e.,
 
   T[3]
 
 should behave much as if it were:
 
   struct ??
   {
      T[3];
   }
 
 Then it can be returned from a function. In particular,
 
   void foo(T[3] a)
 
 is currently done (as in C) by passing a pointer to the array, and then 
 with a bit of compiler magic 'a' is rewritten as (*a)[3]. Making this 
 change would mean that the entire array would be pushed onto the 
 parameter stack, i.e. a copy of the array, rather than a reference to it.
 
 Making this change would clean up the internal behavior of types. 
 They'll be more orthogonal and consistent, and templates will work better.
 
 The previous behavior for function parameters can be retained by making 
 it a ref parameter:
 
    void foo(ref T[3] a)
I don't know why people are agreeing about this. At least I don't understand what the problem is with static arrays. You say: "Currently, static arrays are (as in C) half-value types and half-reference types. This tends to cause a series of weird problems and special cases in the language semantics, such as functions not being able to return static arrays, and out parameters not being possible to be static arrays." But WHY??? What's the specific problem? I understand that passing things by value would solve this, but will hurt performance and it's not in sync with "arrays are passed by reference".
Oct 20 2009
prev sibling next sibling parent Kagamin <spam here.lot> writes:
Jason House Wrote:

 I've never heard the argument why they should be value types.
Weren't they value types from the start? That's surprise. What do you think is memory layout of such array: int[3][] ? And what is memory layout of int[] ?
Oct 20 2009
prev sibling next sibling parent Kagamin <spam here.lot> writes:
Walter Bright Wrote:

 Andrei and I agonized over this for some time, and eventually came to 
 the conclusion that static arrays should become value types.
Nothing to agonize about really (except for C compatibility), they're value types and their behavior must be consistent.
Oct 20 2009
prev sibling next sibling parent Max Samukha <spambox d-coding.com> writes:
On Mon, 19 Oct 2009 18:50:46 -0700, Walter Bright
<newshound1 digitalmars.com> wrote:

Currently, static arrays are (as in C) half-value types and 
half-reference types. This tends to cause a series of weird problems and 
special cases in the language semantics, such as functions not being 
able to return static arrays, and out parameters not being possible to 
be static arrays.

Andrei and I agonized over this for some time, and eventually came to 
the conclusion that static arrays should become value types. I.e.,

   T[3]

should behave much as if it were:

   struct ??
   {
      T[3];
   }

Then it can be returned from a function. In particular,

   void foo(T[3] a)

is currently done (as in C) by passing a pointer to the array, and then 
with a bit of compiler magic 'a' is rewritten as (*a)[3]. Making this 
change would mean that the entire array would be pushed onto the 
parameter stack, i.e. a copy of the array, rather than a reference to it.

Making this change would clean up the internal behavior of types. 
They'll be more orthogonal and consistent, and templates will work better.

The previous behavior for function parameters can be retained by making 
it a ref parameter:

    void foo(ref T[3] a)
Hooah! I guess their .init value won't be fixed to be consistent with other types?
Oct 20 2009
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 19 Oct 2009 21:50:46 -0400, Walter Bright  
<newshound1 digitalmars.com> wrote:

 Currently, static arrays are (as in C) half-value types and  
 half-reference types. This tends to cause a series of weird problems and  
 special cases in the language semantics, such as functions not being  
 able to return static arrays, and out parameters not being possible to  
 be static arrays.

 Andrei and I agonized over this for some time, and eventually came to  
 the conclusion that static arrays should become value types. I.e.,

    T[3]

 should behave much as if it were:

    struct ??
    {
       T[3];
    }

 Then it can be returned from a function. In particular,

    void foo(T[3] a)

 is currently done (as in C) by passing a pointer to the array, and then  
 with a bit of compiler magic 'a' is rewritten as (*a)[3]. Making this  
 change would mean that the entire array would be pushed onto the  
 parameter stack, i.e. a copy of the array, rather than a reference to it.

 Making this change would clean up the internal behavior of types.  
 They'll be more orthogonal and consistent, and templates will work  
 better.

 The previous behavior for function parameters can be retained by making  
 it a ref parameter:

     void foo(ref T[3] a)
What happens for IFTI? void foo(T)(T t) { return t[2]; } void main() { int[3] x; x[] = 5; printf(foo(x)); } I would think T would resolve to int[3], which means pass by value. You'd need a specialization for static arrays to get the current behavior. Don't get me wrong, I would love to see static arrays become real types, but I wonder if there are any ways we can "optimize out" the staticness of an array argument for templates. In particular, I hate how IFTI likes to assume static array for literals... In the absence of such an optimization, I'd still prefer static arrays become value types like you say. -Steve
Oct 20 2009
parent dsimcha <dsimcha yahoo.com> writes:
== Quote from Steven Schveighoffer (schveiguy yahoo.com)'s article
 On Mon, 19 Oct 2009 21:50:46 -0400, Walter Bright
 <newshound1 digitalmars.com> wrote:
 Currently, static arrays are (as in C) half-value types and
 half-reference types. This tends to cause a series of weird problems and
 special cases in the language semantics, such as functions not being
 able to return static arrays, and out parameters not being possible to
 be static arrays.

 Andrei and I agonized over this for some time, and eventually came to
 the conclusion that static arrays should become value types. I.e.,

    T[3]

 should behave much as if it were:

    struct ??
    {
       T[3];
    }

 Then it can be returned from a function. In particular,

    void foo(T[3] a)

 is currently done (as in C) by passing a pointer to the array, and then
 with a bit of compiler magic 'a' is rewritten as (*a)[3]. Making this
 change would mean that the entire array would be pushed onto the
 parameter stack, i.e. a copy of the array, rather than a reference to it.

 Making this change would clean up the internal behavior of types.
 They'll be more orthogonal and consistent, and templates will work
 better.

 The previous behavior for function parameters can be retained by making
 it a ref parameter:

     void foo(ref T[3] a)
What happens for IFTI? void foo(T)(T t) { return t[2]; } void main() { int[3] x; x[] = 5; printf(foo(x)); } I would think T would resolve to int[3], which means pass by value. You'd need a specialization for static arrays to get the current behavior. Don't get me wrong, I would love to see static arrays become real types, but I wonder if there are any ways we can "optimize out" the staticness of an array argument for templates. In particular, I hate how IFTI likes to assume static array for literals... In the absence of such an optimization, I'd still prefer static arrays become value types like you say. -Steve
To me, static arrays are an optimization that you don't use unless you really need it. Dynamic arrays should be most programmers' "default" array type. If you insist on using static arrays, then the onus should be on you to make sure nothing like this happens by doing something like: print(foo(x[])); // Slice operator converts x into an int[], passed //the way dynamic arrays are. The what type are literals question, though, is a legit issue.
Oct 20 2009
prev sibling parent bearophile <bearophileHUGS lycos.com> writes:
Sorry for the late answer, I have missed your answer until now.

dsimcha:

 Here's a way around that:  To pass a static array by reference to a templated
 function that was written with generic ranges in mind, just slice it to make
it a
 dynamic array:
 float[3] foo;
 pragma(msg, typeof(foo[]).stringof);  // float[]
Or you can also pass a pointer to the first item of the fixed-size array: bar(foo.ptr); This change (static arrays becoming value types) will make D less similar to C, so porting C code to D will be less easy (because if in the original C code an array is passed, now the code is slower, and changes to the array can't be seen outside of the function). I think as time passes, D may become less and less compatible with C (and this is has both advantages and disadvantages). A possible solution is to add a new kind of modules to D, the C-like ones, where semantics is kept closer to C (and where switch() has fall-through, etc). No one elase has commented that silly idea of the .ref I have shown. Maybe it's a wrong idea. Bye, bearophile
Oct 24 2009