View mode: basic / threaded / horizontal-split · Log in · Help
July 12, 2005
AA revisited
I have to say the AA changes are confusing to say the least.

With classes using  'lookup[key]'  would create an (key,value) *entry* 
but not an *object*, so you still must check for null and create if 
necessary.  The amount of code here is basically the same whether you 
use *in* or just aa[key].  Since you are using an object reference, you 
can change the object in the AA directly and there is no real need for 
pointers.

We now have 3 possibilities for AA which return classes:

1. Key is not in the AA, must check for this or Exception thrown.
2. Key is in the AA, but the reference stored there is null.
3. Key is in the AA, reference points to an object.

void CallMethod( MyClass c, int key, MyClass[int]  lookup )
{
  MyClass* ptr = (key in lookup);
  if ( ptr is null )  lookup[key] = new MyClass();

  MyClass ref = lookup[key];
  if ( ref is null )
  {
    ref = new MyClass();
    lookup[key]= ref;
  }
  ref.Method();
}

This is starting to make C++ look clean.

Structs on the other hand are supposed to be implicitly created.
They are *values* not objects.

So now we have 2 coding idioms/paradigms for structs:
1. Implicitly create when declared in code.

struct S {
	int n = 99;
	void callme(){ n++ ; }
}

S var1, var2, var3;  // created as declared
var1.callme();       // no problem, I want the defaults, I defined them

2. Struct value in an AA.

S[int] lookup;
S[100].callme() ; //Now this statement requires 7-10 lines of code.

All this because someone was concerned with a double lookup the *first 
time* an object isnt found in the AA?  Semantical madness over something 
that isnt even a real deficiency.

Premature optimization is a root cause of bad programming, because you 
end up making hard to follow code in attempts to optimize before you 
even know what is slow.

This is premature optimization at the *language level*.
-DavidM
July 12, 2005
Re: AA revisited
In article <db0p66$vfj$1@digitaldaemon.com>, David Medlock says...
>
>I have to say the AA changes are confusing to say the least.
>
>With classes using  'lookup[key]'  would create an (key,value) *entry* 
>but not an *object*, so you still must check for null and create if 
>necessary.  The amount of code here is basically the same whether you 
>use *in* or just aa[key].  Since you are using an object reference, you 
>can change the object in the AA directly and there is no real need for 
>pointers.
>
>We now have 3 possibilities for AA which return classes:
>
>1. Key is not in the AA, must check for this or Exception thrown.
>2. Key is in the AA, but the reference stored there is null.
>3. Key is in the AA, reference points to an object.
>
>void CallMethod( MyClass c, int key, MyClass[int]  lookup )
>{
>   MyClass* ptr = (key in lookup);
>   if ( ptr is null )  lookup[key] = new MyClass();
>
>   MyClass ref = lookup[key];
>   if ( ref is null )
>   {
>     ref = new MyClass();
>     lookup[key]= ref;
>   }
>   ref.Method();
>}
>
>This is starting to make C++ look clean.
>
>Structs on the other hand are supposed to be implicitly created.
>They are *values* not objects.
>
>So now we have 2 coding idioms/paradigms for structs:
>1. Implicitly create when declared in code.
>
>struct S {
>	int n = 99;
>	void callme(){ n++ ; }
>}
>
>S var1, var2, var3;  // created as declared
>var1.callme();       // no problem, I want the defaults, I defined them
>
>2. Struct value in an AA.
>
>S[int] lookup;
>S[100].callme() ; //Now this statement requires 7-10 lines of code.
>
>All this because someone was concerned with a double lookup the *first 
>time* an object isnt found in the AA?  Semantical madness over something 
>that isnt even a real deficiency.
>
>Premature optimization is a root cause of bad programming, because you 
>end up making hard to follow code in attempts to optimize before you 
>even know what is slow.
>
>This is premature optimization at the *language level*.
>-DavidM

Some of the code you mentioned has already been deprecated.

aa[key] now throws an exception if it does not exist.  Thus aa[newkey] = new
Class is now invalid.  You must instead call aa.Add(key) 

(See changes for DMD 126: Now throws an ArrayBoundsError if accessing an
associative array with a key that is not already in the array. Previously, the
key would be added to the array. )

Thus your code would change to something like this

:try {
:    foo = aa[key];
:} catch ( Exception e ) {
:    foo = aa.Add(key, new MyClass());
:} finally {
:    foo.Method();
:}

Error handling sucks.  Personally I think AA syntax is starting to transcend
integral types.  Since when does an integer throw an exception?  I'm all for
making AA's a part of phobos and separating them from the language syntax.  


Also, these silly functions properties that are accessed like members are very
confusing.   Like this:


void HelloWorld(int foo) {
writeln("Hi world, I have %d foos", foo);
}

int foo;

foo.HelloWorld();

^-- AAARGGGH!!!  Confusion ensues!  I'd like to see this syntax go away too.
It's not that much effort for people who want to extend the integer type to type
this:

HelloWorld(foo);  (which is less typing By the way, notice the lack of the extra
period)

Which is how it belongs since it's not part of the integer type, and you can't
find it in the class file for integer.
July 12, 2005
Re: AA revisited
Shammah Chancellor wrote:
> In article <db0p66$vfj$1@digitaldaemon.com>, David Medlock says...
> 
>>I have to say the AA changes are confusing to say the least.
>>
>>With classes using  'lookup[key]'  would create an (key,value) *entry* 
>>but not an *object*, so you still must check for null and create if 
>>necessary.  The amount of code here is basically the same whether you 
>>use *in* or just aa[key].  Since you are using an object reference, you 
>>can change the object in the AA directly and there is no real need for 
>>pointers.
>>
>>We now have 3 possibilities for AA which return classes:
>>
>>1. Key is not in the AA, must check for this or Exception thrown.
>>2. Key is in the AA, but the reference stored there is null.
>>3. Key is in the AA, reference points to an object.
>>
>>void CallMethod( MyClass c, int key, MyClass[int]  lookup )
>>{
>>  MyClass* ptr = (key in lookup);
>>  if ( ptr is null )  lookup[key] = new MyClass();
>>
>>  MyClass ref = lookup[key];
>>  if ( ref is null )
>>  {
>>    ref = new MyClass();
>>    lookup[key]= ref;
>>  }
>>  ref.Method();
>>}
>>
>>This is starting to make C++ look clean.
>>
>>Structs on the other hand are supposed to be implicitly created.
>>They are *values* not objects.
>>
>>So now we have 2 coding idioms/paradigms for structs:
>>1. Implicitly create when declared in code.
>>
>>struct S {
>>	int n = 99;
>>	void callme(){ n++ ; }
>>}
>>
>>S var1, var2, var3;  // created as declared
>>var1.callme();       // no problem, I want the defaults, I defined them
>>
>>2. Struct value in an AA.
>>
>>S[int] lookup;
>>S[100].callme() ; //Now this statement requires 7-10 lines of code.
>>
>>All this because someone was concerned with a double lookup the *first 
>>time* an object isnt found in the AA?  Semantical madness over something 
>>that isnt even a real deficiency.
>>
>>Premature optimization is a root cause of bad programming, because you 
>>end up making hard to follow code in attempts to optimize before you 
>>even know what is slow.
>>
>>This is premature optimization at the *language level*.
>>-DavidM
> 
> 
> Some of the code you mentioned has already been deprecated.
> 
> aa[key] now throws an exception if it does not exist.  Thus aa[newkey] = new
> Class is now invalid.  You must instead call aa.Add(key) 
> 

Where is this documented?

http://www.digitalmars.com/d/arrays.html#associative

I see examples of

int[char[]] b;		// associative array b of ints that are
				// indexed by an array of characters.
				// The KeyType is char[]
b["hello"] = 3;		// set value associated with key "hello" to 3

> (See changes for DMD 126: Now throws an ArrayBoundsError if accessing an
> associative array with a key that is not already in the array. Previously, the
> key would be added to the array. )
> 
> Thus your code would change to something like this
> 
> :try {
> :    foo = aa[key];
> :} catch ( Exception e ) {
> :    foo = aa.Add(key, new MyClass());
> :} finally {
> :    foo.Method();
> :}
> 
> Error handling sucks.  Personally I think AA syntax is starting to transcend
> integral types.  Since when does an integer throw an exception?  I'm all for
> making AA's a part of phobos and separating them from the language syntax.  

That is better than the previous semantics?
Compared to what? Java?  I thought D was supposed to be attracting C/C++ 
programmers.  Now we have checked exceptions?

> 
> 
> Also, these silly functions properties that are accessed like members are very
> confusing.   Like this:
> 
> 
> void HelloWorld(int foo) {
> writeln("Hi world, I have %d foos", foo);
> }
> 
> int foo;
> 
> foo.HelloWorld();
> 
> ^-- AAARGGGH!!!  Confusion ensues!  I'd like to see this syntax go away too.
> It's not that much effort for people who want to extend the integer type to type
> this:
> 
> HelloWorld(foo);  (which is less typing By the way, notice the lack of the extra
> period)
> 
> Which is how it belongs since it's not part of the integer type, and you can't
> find it in the class file for integer. 
> 
> 
> 

I agree you can go overboard with built in methods, however for 
templates they are more valuable because they can be overloaded in a 
class, whereas you cannot overload a specialized operator like *in*.
July 12, 2005
Re: AA revisited
> void CallMethod( MyClass c, int key, MyClass[int]  lookup )
> {
>   MyClass* ptr = (key in lookup);
>   if ( ptr is null )  lookup[key] = new MyClass();
>
>   MyClass ref = lookup[key];
>   if ( ref is null )
>   {
>     ref = new MyClass();
>     lookup[key]= ref;
>   }
>   ref.Method();
> }

Once the bug that you found with & is fixed the above can be rewritten as
void CallMethod( int key, MyClass[int]  lookup )
{
  MyClass* ptr = &lookup[key];
  if ( !*ptr ) *ptr = new MyClass;
  *ptr.Method();
}


> This is starting to make C++ look clean.

Again assuming the & bug is fixed the C++ code
map<A,B> x;
B& ref = x[a];
can be rewritten to the D code
B[A] x;
B* ref = &x[a];
That is about as simple as one can expect.

> Structs on the other hand are supposed to be implicitly created.
> They are *values* not objects.
>
> So now we have 2 coding idioms/paradigms for structs:
> 1. Implicitly create when declared in code.
>
> struct S {
> int n = 99;
> void callme(){ n++ ; }
> }
>
> S var1, var2, var3;  // created as declared
> var1.callme();       // no problem, I want the defaults, I defined them
>
> 2. Struct value in an AA.
>
> S[int] lookup;
> S[100].callme() ; //Now this statement requires 7-10 lines of code.
>
> All this because someone was concerned with a double lookup the *first 
> time* an object isnt found in the AA?  Semantical madness over something 
> that isnt even a real deficiency.
>
> Premature optimization is a root cause of bad programming, because you end 
> up making hard to follow code in attempts to optimize before you even know 
> what is slow.
>
> This is premature optimization at the *language level*.
> -DavidM

Somewhere I had read that C++ chose the lookup-and-insert behavior because 
throwing an exception was too slow and has spotty support. In any case I 
know BS said that operator[] returns a reference because returning the value 
is too slow. So there are a couple of C++ examples where performance drove 
the design.
July 12, 2005
Re: AA revisited
Ben Hinkle wrote:

>>void CallMethod( MyClass c, int key, MyClass[int]  lookup )
>>{
>>  MyClass* ptr = (key in lookup);
>>  if ( ptr is null )  lookup[key] = new MyClass();
>>
>>  MyClass ref = lookup[key];
>>  if ( ref is null )
>>  {
>>    ref = new MyClass();
>>    lookup[key]= ref;
>>  }
>>  ref.Method();
>>}
> 
> 
> Once the bug that you found with & is fixed the above can be rewritten as
> void CallMethod( int key, MyClass[int]  lookup )
> {
>    MyClass* ptr = &lookup[key];
>    if ( !*ptr ) *ptr = new MyClass;
>    *ptr.Method();
> }
> 
> 
> 
>>This is starting to make C++ look clean.
> 
> 
> Again assuming the & bug is fixed the C++ code
>  map<A,B> x;
>  B& ref = x[a];
> can be rewritten to the D code
>  B[A] x;
>  B* ref = &x[a];
> That is about as simple as one can expect.
> 
> 
That's beginning to look like C++.
Besides you haven't shown how that is superior to the old way.

What if I want value semantics and not call by reference?
(Which is the whole point of using a struct vs a class )
B* ref = &x[a];
B  temp = ref[0];
  // now I can use my local copy and reinsert it if needed

// versus

B temp = x[a];

The point is the change as made the code *more* complex, with zero 
benefits whatsoever.  If the double lookup is a non-issue because it 
only matters when the value is null (ie the first time).

If you look up the *in stinks* thread, Matthew starts with complaints 
that the in operator returned a pointer.  I actually agree with his 
sentiment, foreach(collections), object references(classes) and out 
parameters(for structs and other values) are superior to pointers.

http://www.digitalmars.com/d/archives/digitalmars/D/18450.html

Now pointers are *required*, and the code to access structures is 
longer(my example) or messy(your example)!!

The better alternative to *in* is a method:

B value;
if ( x.get( in a, out value ) ) { value.dostuff(); }

That is as simple as it gets, no pointers, can still use the create 
semantics, and if 'x' is a template alias, the x can implement .get(...) 
and still function with the don't create semantics.
I can't overload your example.

-DavidM
July 12, 2005
Re: AA revisited
In article <db0ucs$14fp$1@digitaldaemon.com>, Ben Hinkle says...
>
>> void CallMethod( MyClass c, int key, MyClass[int]  lookup )
>> {
>>   MyClass* ptr = (key in lookup);
>>   if ( ptr is null )  lookup[key] = new MyClass();
>>
>>   MyClass ref = lookup[key];
>>   if ( ref is null )
>>   {
>>     ref = new MyClass();
>>     lookup[key]= ref;
>>   }
>>   ref.Method();
>> }
>
>Once the bug that you found with & is fixed the above can be rewritten as
>void CallMethod( int key, MyClass[int]  lookup )
>{
>   MyClass* ptr = &lookup[key];
>   if ( !*ptr ) *ptr = new MyClass;
>   *ptr.Method();
>}

This will no longer work as lookup[key] will throw an exception since of
creating the key since DMD 0.126. If Add will replace existing values, or if
there is a Replace method then you could still simplify it to something very
close to what you have.
July 12, 2005
Re: AA revisited
"David Medlock" <noone@nowhere.com> wrote in message 
news:db0vub$15ot$1@digitaldaemon.com...
> Ben Hinkle wrote:
>
>>>void CallMethod( MyClass c, int key, MyClass[int]  lookup )
>>>{
>>>  MyClass* ptr = (key in lookup);
>>>  if ( ptr is null )  lookup[key] = new MyClass();
>>>
>>>  MyClass ref = lookup[key];
>>>  if ( ref is null )
>>>  {
>>>    ref = new MyClass();
>>>    lookup[key]= ref;
>>>  }
>>>  ref.Method();
>>>}
>>
>>
>> Once the bug that you found with & is fixed the above can be rewritten as
>> void CallMethod( int key, MyClass[int]  lookup )
>> {
>>    MyClass* ptr = &lookup[key];
>>    if ( !*ptr ) *ptr = new MyClass;
>>    *ptr.Method();
>> }
>>
>>
>>
>>>This is starting to make C++ look clean.
>>
>>
>> Again assuming the & bug is fixed the C++ code
>>  map<A,B> x;
>>  B& ref = x[a];
>> can be rewritten to the D code
>>  B[A] x;
>>  B* ref = &x[a];
>> That is about as simple as one can expect.
>>
>>
> That's beginning to look like C++.
> Besides you haven't shown how that is superior to the old way.

I'm not sure what you mean by "old way". Can you be more specific?

> What if I want value semantics and not call by reference?
> (Which is the whole point of using a struct vs a class )
> B* ref = &x[a];
> B  temp = ref[0];
>   // now I can use my local copy and reinsert it if needed
>
> // versus
>
> B temp = x[a];
>
> The point is the change as made the code *more* complex, with zero 
> benefits whatsoever.  If the double lookup is a non-issue because it only 
> matters when the value is null (ie the first time).

umm - "zero benefit"? whatever...
I agree it would be nice to have an explicit method to insert-and-lookup 
when that is what one wants as was discussed in the posts on the bug thread 
about &.

> If you look up the *in stinks* thread, Matthew starts with complaints that 
> the in operator returned a pointer.  I actually agree with his sentiment, 
> foreach(collections), object references(classes) and out parameters(for 
> structs and other values) are superior to pointers.
>
> http://www.digitalmars.com/d/archives/digitalmars/D/18450.html
>
> Now pointers are *required*, and the code to access structures is 
> longer(my example) or messy(your example)!!
>
> The better alternative to *in* is a method:
>
> B value;
> if ( x.get( in a, out value ) ) { value.dostuff(); }
>
> That is as simple as it gets, no pointers, can still use the create 
> semantics, and if 'x' is a template alias, the x can implement .get(...) 
> and still function with the don't create semantics.


Various posts (I included it in my API requests) had what you call "get" by 
the name "contains" (though to be honest I've forgotten the details). I 
personally don't have anything against pointers, though, so the current 'in' 
is ok with me.

> I can't overload your example.

I, too, would like more overload control than what is available today. See 
the posts about opIndexMutable and such started by Kevin Bealer, for 
example.
July 12, 2005
Re: AA revisited
>That's beginning to look like C++.
>Besides you haven't shown how that is superior to the old way.
>
>What if I want value semantics and not call by reference?
>(Which is the whole point of using a struct vs a class )
>B* ref = &x[a];
>B  temp = ref[0];
>   // now I can use my local copy and reinsert it if needed
>
>// versus
>
>B temp = x[a];
>
>The point is the change as made the code *more* complex, with zero 
>benefits whatsoever.  If the double lookup is a non-issue because it 
>only matters when the value is null (ie the first time).
>
>If you look up the *in stinks* thread, Matthew starts with complaints 
>that the in operator returned a pointer.  I actually agree with his 
>sentiment, foreach(collections), object references(classes) and out 
>parameters(for structs and other values) are superior to pointers.
>
>http://www.digitalmars.com/d/archives/digitalmars/D/18450.html
>
>Now pointers are *required*, and the code to access structures is 
>longer(my example) or messy(your example)!!
>
>The better alternative to *in* is a method:
>
>B value;
>if ( x.get( in a, out value ) ) { value.dostuff(); }
>
>That is as simple as it gets, no pointers, can still use the create 
>semantics, and if 'x' is a template alias, the x can implement .get(...) 
>and still function with the don't create semantics.
>I can't overload your example.
>
>-DavidM


Not to mention we have some context dependant syntax going on there with the in
keyword being used for different things in different places.  IE:

int foo( in int a, inout int[int] foo ) {
MyClass *arg = a in foo;
}

See the different use?  I vote to make AA it's own class....
July 12, 2005
Re: AA revisited
>>Once the bug that you found with & is fixed the above can be rewritten as
>>void CallMethod( int key, MyClass[int]  lookup )
>>{
>>   MyClass* ptr = &lookup[key];
>>   if ( !*ptr ) *ptr = new MyClass;
>>   *ptr.Method();
>>}
>
> This will no longer work as lookup[key] will throw an exception since of
> creating the key since DMD 0.126. If Add will replace existing values, or 
> if
> there is a Replace method then you could still simplify it to something 
> very
> close to what you have.

lookup[key] does not throw if the result is expected to be an lvalue. The 
problem is that there is a bug in dmd that the & operator isn't among those 
operators that are flagged as expecting an lvalue. See David's earlier post 
in this newsgroup and see my post about it in the bugs newsgroup.
July 12, 2005
Re: AA revisited
Here is an example of code which was elegant, and now must be messied up 
because of the changes in AA(at bottom).

My choices now are to
A) switch everyhing to pointers and use &keys[...]
B) add functions which create when needed in the arrays
C) use a function to add all the keys on startup (Not feasible, as keys 
are updated as they are queried)

I think the code below is fairly elegant, but it will not be under the 
new AA.


// This code uses the glfw framework to check Keys and Mouse Movement
import glfw;

import std.stdio;

public int                   MouseX, MouseY, MouseZ, MouseDX, MouseDY, 
MouseDZ;


enum Mouse { Left=GLFW_MOUSE_BUTTON_LEFT, Right=GLFW_MOUSE_BUTTON_RIGHT, 
Middle=GLFW_MOUSE_BUTTON_MIDDLE };
enum Key { F1=GLFW_KEY_F1, F2=GLFW_KEY_F2, F3=GLFW_KEY_F3, F4=GLFW_KEY_F4 };


private:
struct keystate
{
  bool    down      = false;
  bool    changed   = false;
  bool    updated   = false;

  bool    released() { return (!down) && changed; }
  bool    pressed()  { return down && changed; }

  protected void update( bool isdown )
  {
    changed = ( down != isdown );
    down = isdown;
    updated = true;
  }
}


keystate[ int ]         keys;
keystate[ int ]         mbutton;


// mouse buttons are updated each Update, so just return the state
keystate    getmouse( int btn ){ return mbutton[ btn ]; }

keystate    getkey( int keycode )
{
  keystate st = keys[keycode];
  if ( !st.updated ) {
    st.update( glfwGetKey( keycode )==GLFW_PRESS );
    keys[keycode] = st;
  }
  return st;
}


public:

void ClearInput()
{
  foreach( int code; keys.keys ) delete keys[code];
  foreach( int code; mbutton.keys ) delete mbutton[code];
  MouseDX = MouseDY = MouseDZ = 0;
}

void CheckInput()
{
  foreach( int kcode; keys.keys )  (kcode in keys).updated = false;

  glfwPollEvents();

  int x, y, z;

  mbutton[Mouse.Left].update( glfwGetMouseButton( Mouse.Left 
)==GLFW_PRESS );
  mbutton[Mouse.Middle].update( glfwGetMouseButton( Mouse.Middle 
)==GLFW_PRESS );
  mbutton[Mouse.Right].update( glfwGetMouseButton( Mouse.Right 
)==GLFW_PRESS );

  z = glfwGetMouseWheel();
  glfwGetMousePos( &x, &y );
  MouseDX = x - MouseX;
  MouseDY = y - MouseY;
  MouseDZ = z - MouseZ;
  MouseX = x;
  MouseY = y;
  MouseZ = z;
}


// key was just pressed
bool    KeyPressed( int code ) { return getkey( code ).pressed; }

// key was just released
bool    KeyReleased( int code ) { return getkey( code ).released;}

// key is currently down
bool    KeyDown( int code ) { return getkey( code ).down ; }

// key is currently up
bool    KeyUp( int code )   { return getkey( code ).down==false; }


// mouse has moved since last update
bool    MouseMove() { return (MouseDX!=0) || (MouseDY!=0) || (MouseDZ!=0 
); }

bool    MouseDown(int btn) { return getmouse( btn ).down; }
bool    MouseUp( int btn ) { return false==getmouse( btn ).down; }
bool    MouseReleased( int btn ) { return getmouse(btn).released; }
bool    MousePressed( int btn )  { return getmouse(btn).pressed; }
« First   ‹ Prev
1 2 3 4
Top | Discussion index | About this forum | D home