www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Aliases

reply Reiner Pope <reiner.pope REMOVE.THIS.gmail.com> writes:
What's the purpose of the restriction of alias in the specs -- 'Aliases 
cannot be used for expressions'; it prevents some useful things like this:

   int[2] nodes;
   alias nodes[0] left;
   alias nodes[1] right;

which currently has to be written in a much more hackish way:

   union {
     int[2] nodes;
     struct {
        int left;
        int right;
     }
   }

This is less reliable because if you decide to change the datatype of 
nodes, you could easily stuff up:

   union {
     long[2] nodes;
     struct {
        int left; // oops, forgot to change this. Now we've sliced the 
first value
        int right;
     }
   }

(Alternatively, you could implement this in an even more verbose way, 
which doesn't support things like left++:
    int[2] nodes;
    int left() { return nodes[0]; }
    int left(int val) { return nodes[0] = val; }
    int right() { return nodes[1]; }
    int right(int val) { return nodes[1] = val; }
)

It could also be used for function forwarding, as Frank Benoit requested 
earlier ('Feat. RQ: delegation alias'):

   class Foo
   {
     void DoSomething() {...}
   }

   class FooHandle
   {
     Foo myFoo;
     alias myFoo.DoSomething DoSomething;
   }


Maybe someone can explain why this restriction is necessary.

Cheers,

Reiner
Oct 22 2006
next sibling parent Hasan Aljudy <hasan.aljudy gmail.com> writes:
Reiner Pope wrote:
 What's the purpose of the restriction of alias in the specs -- 'Aliases 
 cannot be used for expressions'; it prevents some useful things like this:
 
   int[2] nodes;
   alias nodes[0] left;
   alias nodes[1] right;
 
 which currently has to be written in a much more hackish way:
 
   union {
     int[2] nodes;
     struct {
        int left;
        int right;
     }
   }
 
 This is less reliable because if you decide to change the datatype of 
 nodes, you could easily stuff up:
 
   union {
     long[2] nodes;
     struct {
        int left; // oops, forgot to change this. Now we've sliced the 
 first value
        int right;
     }
   }
 
 (Alternatively, you could implement this in an even more verbose way, 
 which doesn't support things like left++:
    int[2] nodes;
    int left() { return nodes[0]; }
    int left(int val) { return nodes[0] = val; }
    int right() { return nodes[1]; }
    int right(int val) { return nodes[1] = val; }
 )
 
 It could also be used for function forwarding, as Frank Benoit requested 
 earlier ('Feat. RQ: delegation alias'):
 
   class Foo
   {
     void DoSomething() {...}
   }
 
   class FooHandle
   {
     Foo myFoo;
     alias myFoo.DoSomething DoSomething;
   }
 
 
 Maybe someone can explain why this restriction is necessary.
alias has to do with symbols in the symbol table. At least that's the definition, IIRC. Although I'm sure it can either be extended to include what you propose, or a new keyword can be introduced for that purpose.
 
 Cheers,
 
 Reiner
Oct 22 2006
prev sibling next sibling parent reply rm <roel.mathys gmail.com> writes:
Reiner Pope wrote:
 What's the purpose of the restriction of alias in the specs -- 'Aliases
 cannot be used for expressions'; it prevents some useful things like this:
 
   int[2] nodes;
   alias nodes[0] left;
   alias nodes[1] right;
 
 which currently has to be written in a much more hackish way:
 
   union {
     int[2] nodes;
     struct {
        int left;
        int right;
     }
   }
 
 This is less reliable because if you decide to change the datatype of
 nodes, you could easily stuff up:
 
   union {
     long[2] nodes;
     struct {
        int left; // oops, forgot to change this. Now we've sliced the
 first value
        int right;
     }
   }
 
you could do this: template KT(T) { union KT { T[2] nodes; struct { T left; T right; } } } alias KT!(int) K; roel
Oct 22 2006
parent reply Bill Baxter <wbaxter gmail.com> writes:
rm wrote:
 Reiner Pope wrote:
 
What's the purpose of the restriction of alias in the specs -- 'Aliases
cannot be used for expressions'; it prevents some useful things like this:

  int[2] nodes;
  alias nodes[0] left;
  alias nodes[1] right;

which currently has to be written in a much more hackish way:

  union {
    int[2] nodes;
    struct {
       int left;
       int right;
    }
  }

This is less reliable because if you decide to change the datatype of
nodes, you could easily stuff up:
you could do this: template KT(T) { union KT { T[2] nodes; struct { T left; T right; } } } alias KT!(int) K; roel
Or in this case he's not even after a template, so just union { alias int NodeID; NoteID[2] nodes; struct { NodeID left; NodeID right; } } But, I see your point, Reiner. 'alias foo[1] bar' expresses the intent of the programmer more clearly here than union, and doesn't have the typesafety issues. --bb
Oct 22 2006
parent Hasan Aljudy <hasan.aljudy gmail.com> writes:
Bill Baxter wrote:
 rm wrote:
 Reiner Pope wrote:

 What's the purpose of the restriction of alias in the specs -- 'Aliases
 cannot be used for expressions'; it prevents some useful things like 
 this:

  int[2] nodes;
  alias nodes[0] left;
  alias nodes[1] right;

 which currently has to be written in a much more hackish way:

  union {
    int[2] nodes;
    struct {
       int left;
       int right;
    }
  }

 This is less reliable because if you decide to change the datatype of
 nodes, you could easily stuff up:
you could do this: template KT(T) { union KT { T[2] nodes; struct { T left; T right; } } } alias KT!(int) K; roel
Or in this case he's not even after a template, so just union { alias int NodeID; NoteID[2] nodes; struct { NodeID left; NodeID right; } } But, I see your point, Reiner. 'alias foo[1] bar' expresses the intent of the programmer more clearly here than union, and doesn't have the typesafety issues. --bb
I think this is a typesafe solution: typeof(node[1]) left; typeof(node[2]) right;
Oct 22 2006
prev sibling next sibling parent "Andrey Khropov" <andkhropov_nosp m_mtu-net.ru> writes:
Reiner Pope wrote:

 What's the purpose of the restriction of alias in the specs -- 'Aliases
 cannot be used for expressions'; it prevents some useful things like this:
 
   int[2] nodes;
   alias nodes[0] left;
   alias nodes[1] right;
Looks kinda scary. It resembles me of C's textual macros which are somewhat dangerous and may introduce hard to debug errors. -- AKhropov
Oct 22 2006
prev sibling parent reply Walter Bright <newshound digitalmars.com> writes:
Reiner Pope wrote:
 What's the purpose of the restriction of alias in the specs -- 'Aliases 
 cannot be used for expressions'; it prevents some useful things like this:
 
   int[2] nodes;
   alias nodes[0] left;
   alias nodes[1] right;
How about: auto left = &nodes[0]; auto right = &nodes[1]; Or: auto left = (){ return nodes[0]; } auto right = (){ return nodes[1]; }
 It could also be used for function forwarding, as Frank Benoit requested 
 earlier ('Feat. RQ: delegation alias'):
 
   class Foo
   {
     void DoSomething() {...}
   }
 
   class FooHandle
   {
     Foo myFoo;
     alias myFoo.DoSomething DoSomething;
   }
Or: auto DoSomething = &myFoo.DoSomething;
 Maybe someone can explain why this restriction is necessary.
Oct 22 2006
next sibling parent reply Bill Baxter <wbaxter gmail.com> writes:
Walter Bright wrote:
 Reiner Pope wrote:
 
 What's the purpose of the restriction of alias in the specs -- 
 'Aliases cannot be used for expressions'; it prevents some useful 
 things like this:

   int[2] nodes;
   alias nodes[0] left;
   alias nodes[1] right;
How about: auto left = &nodes[0]; auto right = &nodes[1];
test.d(13): non-constant expression & nodes test.d(14): non-constant expression (& nodes+4)
 
 Or:
 
     auto left = (){ return nodes[0]; }
     auto right = (){ return nodes[1]; }
auto left = (){ return nodes[0]; }; auto right = (){ return nodes[1]; }; test.d(15): delegate properties.Props.__dgliteral1 literals cannot be class members <COMPILER CRASH>
 
 It could also be used for function forwarding, as Frank Benoit 
 requested earlier ('Feat. RQ: delegation alias'):

   class Foo
   {
     void DoSomething() {...}
   }

   class FooHandle
   {
     Foo myFoo;
     alias myFoo.DoSomething DoSomething;
   }
Or: auto DoSomething = &myFoo.DoSomething;
test.d(32): non-constant expression &(myFoo).DoSomething --bb
Oct 22 2006
parent reply Walter Bright <newshound digitalmars.com> writes:
RE: errors

I wrote those on the assumption they'd be inside a function. As globals, 
they won't work, but in general things line int[2] shouldn't be globals.
Oct 22 2006
next sibling parent Hasan Aljudy <hasan.aljudy gmail.com> writes:
Walter Bright wrote:
 RE: errors
 
 I wrote those on the assumption they'd be inside a function. As globals, 
 they won't work, but in general things line int[2] shouldn't be globals.
I think the point was that these workarounds aren't sufficient.
Oct 22 2006
prev sibling parent reply Bill Baxter <wbaxter gmail.com> writes:
Walter Bright wrote:
 RE: errors
 
 I wrote those on the assumption they'd be inside a function. As globals, 
 they won't work, but in general things line int[2] shouldn't be globals.
Those were in a class. --bb
Oct 22 2006
parent reply Walter Bright <newshound digitalmars.com> writes:
Bill Baxter wrote:
 Walter Bright wrote:
 RE: errors

 I wrote those on the assumption they'd be inside a function. As 
 globals, they won't work, but in general things line int[2] shouldn't 
 be globals.
Those were in a class.
Glad you told me that, because I couldn't duplicate the errors. The first one works as:
 class Foo
 {
     int[2] nodes;
     int left() { return nodes[0]; }
     int right() { return nodes[1]; }
 }
And the other:
 class Foo
   {
     void DoSomething() { }
   }
 
   class FooHandle
   {
     Foo myFoo;
     void DoSomething() { myFoo.DoSomething(); }
   }
Oct 23 2006
parent reply Reiner Pope <reiner.pope REMOVE.THIS.gmail.com> writes:
Walter Bright wrote:
 The first one works as:
 
 class Foo
 {
     int[2] nodes;
     int left() { return nodes[0]; }
     int right() { return nodes[1]; }
 }
Except that this doesn't support writing of the nodes. For that, you would need to write property setters as well, making the code: class Foo { int[2] nodes; int left() { return nodes[0]; } int left(int val) { return nodes[0] = val; } int right() { return nodes[1]; } int right(int val) { return nodes[1] = val; } } which is much more verbose than the (impossible) alias version: class Foo { int[2] nodes; alias nodes[0] left; alias nodes[1] right; } Never worry, I thought I would try to make a mixin that supports it: template readerWriter(alias b) { private alias typeof(b) T; T rw() { return b; } T rw(T val) { return b = val; } } struct Foo { int[2] stuff; mixin readerWriter!(stuff[0]) _left; alias _left.rw left; mixin readerWriter!(stuff[1]) _right; alias _right.rw right; } Except for the renaming-of-mixins issue (that argument's for another day), the code would have been pretty simple, except that it doesn't work for pretty much the same reason -- you can't alias this kind of thing. And, of course, you have all the normal property issues: - you can't be sure they'll be inlined - you can't do left++ - you can't do 'auto val = left;'
 
 And the other:
 
 class Foo
   {
     void DoSomething() { }
   }

   class FooHandle
   {
     Foo myFoo;
     void DoSomething() { myFoo.DoSomething(); }
   }
I agree, there's not much problem with this, but here it's a question of convenience and consistency: we can use alias to forward local functions (presumably because you decided it wouldn't be too hard to do, and changing the signatures is a pain which could be avoided) yet we can't use it to forward non-local functions. In fact, it can get much more complex than that, if you have a lot of overloads, or a function signature which changes a lot: class MyPrinter { void print(int) {} void print(char) {} void print(char[]) {} ... } class PrinterHandle { MyPrinter p; alias p.print print; } as opposed to class PrinterHandle2 { MyPrinter p; void print(int val) { p.print(val); } void print(char val) { p.print(val); } void print(char[] val) { p.print(val); } ... } While I do think that (if just for consistency's sake) we shouldn't have this restriction on aliases, I realise that this is a problem of syntactical optimization, and the best approach (again) would be to embody a form of metaprogramming which could support this sort of thing in libraries because this is not the only syntactical inefficiency in the language, and we need some much more extensible way to remove such inefficiencies; I think Nemerle could be a good place to look for such a metaprogramming/macro system. Cheers, Reiner
Oct 23 2006
parent Walter Bright <newshound digitalmars.com> writes:
Reiner Pope wrote:
 While I do think that (if just for consistency's sake) we shouldn't have 
 this restriction on aliases, I realise that this is a problem of 
 syntactical optimization, and the best approach (again) would be to 
 embody a form of metaprogramming which could support this sort of thing 
 in libraries because this is not the only syntactical inefficiency in 
 the language, and we need some much more extensible way to remove such 
 inefficiencies; I think Nemerle could be a good place to look for such a 
 metaprogramming/macro system.
I agree with all of your points. Whether it is worth adding more to D to account for it basically depends on how often this happens, and how onerous are the workarounds.
Oct 24 2006
prev sibling parent BCS <BCS pathlink.com> writes:
All of the below add members or variables to whatever they are in. The 
alias solution is 100% compile time, and has no effect on the structure 
layout.

Walter Bright wrote:
 Reiner Pope wrote:
 
   int[2] nodes;
   alias nodes[0] left;
   alias nodes[1] right;
How about: auto left = &nodes[0]; auto right = &nodes[1]; Or: auto left = (){ return nodes[0]; } auto right = (){ return nodes[1]; }
   class Foo
   {
     void DoSomething() {...}
   }

   class FooHandle
   {
     Foo myFoo;
     alias myFoo.DoSomething DoSomething;
   }
Or: auto DoSomething = &myFoo.DoSomething;
This one might be non-doable with the alias as it will almost certainly requirer some translation code.
Oct 23 2006