www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Am I getting this all right?

reply Jason House <jhouse mitre.org> writes:
I'm kind of doing a trial by fire method to learn D and how it works... I'm
taking an existing open source project and converting it over.  Here's the
stuff that I've found / questions that I have.

Thanks in advance for all the thoughtful responses.  I know this is a long
post!  I've worked through the issues to get working code, but I think my
issues will be hit by others.

It seems that DTL and minTL are the two candidates for an STL equivalent in D.
 Both seem to be out of date.  Since minTL is documented, I chose to use that.
 I found that the latest dmd (0.176) doesn't compile the latest minTL.  It
complains that va_arg isn't defined even though it imports std.stdarg.  Adding
a fully qualified name fixed my usage problem.  Is this really the best option
that I have for STL equivalent?  Anyone else compiling this stuff and hitting
problems?

I know of no way in d to provide a const qualifier.  It seems like the concept
purely doesn't exist.  minTL appears to have a workaround where it templates
itself on a boolean value, and simply doesn't publish certain functions.  When
will a real const qualifier get added to the language?  It seems really
strange that a language that adds all kinds of contract programming wouldn't
use const qualifiers / const types.

After banging my head on printing stuff for a long time, I've come to a few
conclusions:
* The C++ equivalent to std::ostream& operator << (std::ostream &out, const
type &x) does not exist
* Custom types use the toString property
* Enums do not (and can not) have a toString property
* I have found no way to determine (in a templated class) if a type is
printable or not.  static if(is(typeof(T))) comes close, but my very first
test using ints failed.

The special unittest member function doesn't get built in with -unittest
unless the actual class is used in some way (and then one copy of the unittest
funciton gets run for each type).  I'd really like to see some way for this to
work.  for example, class foo(T){ ... unittest{ foo!(int) sample; ...} } does
not run unless other code instantiates foo!(U)

In my early efforts at doing operator overloading, I wrote opCmp and then was
shocked when my unit test showed that == didn't even call opCmp.  It silently
did something wrong.  This seems like a gotcha built into the language that
should probably get remedied.

I couldn't find a way to reinitialize a variable back to a default value...
For example class foo(T){ T[] bar; void func(){bar[3] = new T();} } won't
compile.  I'm not too sure why this is the case.
Dec 13 2006
next sibling parent reply Sean Kelly <sean f4.ca> writes:
Jason House wrote:
 I'm kind of doing a trial by fire method to learn D and how it works... I'm
 taking an existing open source project and converting it over.  Here's the
 stuff that I've found / questions that I have.
 
 Thanks in advance for all the thoughtful responses.  I know this is a long
 post!  I've worked through the issues to get working code, but I think my
 issues will be hit by others.
 
 It seems that DTL and minTL are the two candidates for an STL equivalent in D.
  Both seem to be out of date.  Since minTL is documented, I chose to use that.
  I found that the latest dmd (0.176) doesn't compile the latest minTL.  It
 complains that va_arg isn't defined even though it imports std.stdarg.  Adding
 a fully qualified name fixed my usage problem.  Is this really the best option
 that I have for STL equivalent?  Anyone else compiling this stuff and hitting
 problems?

Check DSource (www.dsource.org). IIRC there is at least one STL replacement project there. Of the above, DTL was created by Matthew Wilson, and hasn't been updated in at least a year. MinTL was created by Ben Hinkle, and has been dormant for almost as long. MinTL is more mature however, and should be easier to update.
 I know of no way in d to provide a const qualifier.  It seems like the concept
 purely doesn't exist.  minTL appears to have a workaround where it templates
 itself on a boolean value, and simply doesn't publish certain functions.  When
 will a real const qualifier get added to the language?  It seems really
 strange that a language that adds all kinds of contract programming wouldn't
 use const qualifiers / const types.

This is correct. In D, 'const' is a storage class, and can not be applied to values at runtime (essentially). D has no run-time const behavior largely because the implementations in expisting popular languages are undesirable (C++ const, for example) for various reasons, and some of the more radical solutions such as const-by-default came up too late in the language's development cycle (as I understand it).
 After banging my head on printing stuff for a long time, I've come to a few
 conclusions:
 * The C++ equivalent to std::ostream& operator << (std::ostream &out, const
 type &x) does not exist

It could. Mango (available on DSource) overloaded these operators for a while, but no one used them and I think they may have been removed. Mango's "whisper syntax" seems preferable in most cases, though it doesn't work for an IOStream concept, while the shift operators do.
 * Custom types use the toString property

Yes, though some of us wish this function were called "toUtf8" to improve clarity. This is an idealistic point however, and not pertinent to the discussion.
 * Enums do not (and can not) have a toString property

They don't at the moment. One could be added, I imagine, but it would have to be done in the compiler.
 * I have found no way to determine (in a templated class) if a type is
 printable or not.  static if(is(typeof(T))) comes close, but my very first
 test using ints failed.

"static if(is(typeof(T.toString)))" should tell you whether the function is defined, if T is a struct or class, but it sounds like you want to test concrete types as well?
 The special unittest member function doesn't get built in with -unittest
 unless the actual class is used in some way (and then one copy of the unittest
 funciton gets run for each type).  I'd really like to see some way for this to
 work.  for example, class foo(T){ ... unittest{ foo!(int) sample; ...} } does
 not run unless other code instantiates foo!(U)

Why not do: class C(T) {} unittest { /* test C here */ } Since private data is actually visible at module scope, the above approach loses nothing over having unittest within the class definition itself.
 In my early efforts at doing operator overloading, I wrote opCmp and then was
 shocked when my unit test showed that == didn't even call opCmp.  It silently
 did something wrong.  This seems like a gotcha built into the language that
 should probably get remedied.

It calls opEquals. Personally, I prefer this approach as it's not uncommon for me to define an ordering scheme that is less strict than I want for equality comparisons.
 I couldn't find a way to reinitialize a variable back to a default value...
 For example class foo(T){ T[] bar; void func(){bar[3] = new T();} } won't
 compile.  I'm not too sure why this is the case.

Try: T[] bar = new T[size]; T[0 .. $] = new T(); You still have to resize and assign in separate operations, but the above should work. Sean
Dec 13 2006
next sibling parent Ary Manzana <ary esperanto.org.ar> writes:
Sean Kelly escribió:
 Jason House wrote:
 I couldn't find a way to reinitialize a variable back to a default 
 value...
 For example class foo(T){ T[] bar; void func(){bar[3] = new T();} } won't
 compile.  I'm not too sure why this is the case.

Try: T[] bar = new T[size]; T[0 .. $] = new T();

It worked for me, if T is a class. If it's a primitive type, you should do "T.init" instead of "new T()". So heres the code: vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv class foo(T) { T[] bar; void func(){ static if (is(T == class)) { bar[3] = new T(); // TODO: what if it's a struct, typedef, etc.? } else { bar[3] = T.init; } } } ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ BTW, I could compile this: vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv class foo(T) { } void main() { mixin foo!(int) a; } ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ but this gives me an error vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv 1. class foo(T) { 2. 3. } 4. 5. void main() { 6. mixin foo!(int) a; 7. mixin foo!(real) b; 8. } ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 main.d(7): mixin foo!(real) foo is not a template

Why?
Dec 13 2006
prev sibling parent reply Jason House <jason.james.house gmail.com> writes:
Sean Kelly wrote:
 Check DSource (www.dsource.org).  IIRC there is at least one STL 
 replacement project there.  Of the above, DTL was created by Matthew 
 Wilson, and hasn't been updated in at least a year.  MinTL was created 
 by Ben Hinkle, and has been dormant for almost as long.  MinTL is more 
 mature however, and should be easier to update.

When poking around that site, I didn't see anything obviously STL related. I've been known to be bline in the past.
 * Custom types use the toString property

Yes, though some of us wish this function were called "toUtf8" to improve clarity. This is an idealistic point however, and not pertinent to the discussion.
 * Enums do not (and can not) have a toString property

They don't at the moment. One could be added, I imagine, but it would have to be done in the compiler.

That's exactly correct... Or at the very least the compiler must allow such properties to be added to them. I did try adding my own toString property (just in case there was neat hidden feature like that)
 * I have found no way to determine (in a templated class) if a type is
 printable or not.  static if(is(typeof(T))) comes close, but my very 
 first
 test using ints failed.

"static if(is(typeof(T.toString)))" should tell you whether the function is defined, if T is a struct or class, but it sounds like you want to test concrete types as well?

Sorry, typo in my original post. What you propose is what I put in the code. Yes, I want to handle concrete types as well.
 Why not do:
 
     class C(T) {}
     unittest { /* test C here */ }
 
 Since private data is actually visible at module scope, the above 
 approach loses nothing over having unittest within the class definition 
 itself.

That's what I have to do, but it seems to be against "the D way" of embedding contracts and unit tests as closely as possible to the true code. I haven't used ddoc, but I bet the alternate method may yield misleading documentation.
 
 In my early efforts at doing operator overloading, I wrote opCmp and 
 then was
 shocked when my unit test showed that == didn't even call opCmp.  It 
 silently
 did something wrong.  This seems like a gotcha built into the language 
 that
 should probably get remedied.

It calls opEquals. Personally, I prefer this approach as it's not uncommon for me to define an ordering scheme that is less strict than I want for equality comparisons.

I can totally see that. But what happens when only opCmp is defined? Should the default opEquals use opCmp or do something else? I was assuming it'd use opCmp. Again, in what I perceive to be the style of D, such oops type things should possibly cause a warning.
 
 I couldn't find a way to reinitialize a variable back to a default 
 value...
 For example class foo(T){ T[] bar; void func(){bar[3] = new T();} } won't
 compile.  I'm not too sure why this is the case.

Try: T[] bar = new T[size]; T[0 .. $] = new T(); You still have to resize and assign in separate operations, but the above should work.

I'll try that. My actual code was essentially T[] bar = new T[size]; for (int i=0; i<size; i++) T[i] = new T(); ... which I totally expect to work. (sorry for leaving out the array sizing in my original example)
Dec 13 2006
next sibling parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Jason House wrote:
 Sean Kelly wrote:
 Check DSource (www.dsource.org).  IIRC there is at least one STL 
 replacement project there.  Of the above, DTL was created by Matthew 
 Wilson, and hasn't been updated in at least a year.  MinTL was created 
 by Ben Hinkle, and has been dormant for almost as long.  MinTL is more 
 mature however, and should be easier to update.

When poking around that site, I didn't see anything obviously STL related. I've been known to be bline in the past.

There is a branch of the Mango tree for containers. http://dsource.org/projects/mango That said, I find that I rarely miss container classes/structs in D. The most common cases are handled by variable-length arrays. (Although I do have pseudo-member functions in Cashew for simulating a stack using an array.) http://dsource.org/projects/cashew # import cashew .utils .array ; # import std .stdio ; # # void main (char[][] args) { # char[][] stack ; # # foreach (x; args) # stack.push(x); # # while (stack.length) # writefln("%s", stack.pop); # } Trees are also very easily achieved in D. If all you need is the basic concept of a tree structure, with breadth-first and depth-first iteration, its a snap. Something like this, for example: # struct Tree(int B = 2, T) { # version (Tree_NoWarnings) {} else # static if (B == 0) # static assert(false, "A tree with zero branches is just useless."); # else static if (B == 1) # static assert(false, "A tree with only one branch may as well be an array."); # # alias Tree!(B, T) Node ; # # Node*[B] children ; # T value ; # # int walkDepth (int delegate(inout Node) dlg) { # int result ; # # foreach (x; children) # if (x && (result = x.walkDepth(dlg))) # goto L_end; # result = dlg(this); # # L_end: # return result; # } # # int walkBreadth (int delegate(inout Node) dlg) { # int result ; # Node*[] gen = [this] , # next ; # # while (gen.length) { # foreach (x; gen) { # if (result = dlg(x.value)) # goto end; # foreach (y; x.children) # if (y) # next ~= y; # } # gen = next.dup; # next.length = 0; # } # # end: # return result; # } # } (Of course there are some things that just seem to work better with elaborate container libraries. Its all according to the needs, as with anything.)
 * Enums do not (and can not) have a toString property

They don't at the moment. One could be added, I imagine, but it would have to be done in the compiler.

That's exactly correct... Or at the very least the compiler must allow such properties to be added to them. I did try adding my own toString property (just in case there was neat hidden feature like that)

No... but you can have a constant associative array, with the enum as the key and a string as the value. This is what I usually do when I want string representations of enum values. For a cheap example: # enum Qubit { # T, F, TT, TF, FT, FF # } # # const char[][Qubit] QubitNames ; # # static this () { # QubitNames[Qubit.T ] = "T" ; # QubitNames[Qubit.F ] = "F" ; # QubitNames[Qubit.TT] = "TT" ; # // ... etc # }
 That's what I have to do, but it seems to be against "the D way" of 
 embedding contracts and unit tests as closely as possible to the true 
 code.  I haven't used ddoc, but I bet the alternate method may yield 
 misleading documentation.

So far as I have personally experienced, DDoc doesn't emit any content for unittests anyhow. So there should be no impact as far as that goes. And since you can have as many unittests in a module as you like, its still pretty "D like" to have a unittest block after each class definition.
 In my early efforts at doing operator overloading, I wrote opCmp and 
 then was
 shocked when my unit test showed that == didn't even call opCmp.  It 
 silently
 did something wrong.  This seems like a gotcha built into the 
 language that
 should probably get remedied.

It calls opEquals. Personally, I prefer this approach as it's not uncommon for me to define an ordering scheme that is less strict than I want for equality comparisons.

I can totally see that. But what happens when only opCmp is defined? Should the default opEquals use opCmp or do something else? I was assuming it'd use opCmp. Again, in what I perceive to be the style of D, such oops type things should possibly cause a warning.

I for one think the default opEquals probably /should/ use opCmp... but alas.
 I couldn't find a way to reinitialize a variable back to a default 
 value...
 For example class foo(T){ T[] bar; void func(){bar[3] = new T();} } 
 won't
 compile.  I'm not too sure why this is the case.

Try: T[] bar = new T[size]; T[0 .. $] = new T(); You still have to resize and assign in separate operations, but the above should work.

I'll try that. My actual code was essentially T[] bar = new T[size]; for (int i=0; i<size; i++) T[i] = new T(); ... which I totally expect to work. (sorry for leaving out the array sizing in my original example)

You can also use... bar[] = new T; ...to emulate the above. If I recall right, array[] as an LValue is the same as array[0..$] as one. However, chances are what you meant to do was to set each element to a /seperate/ object, rather than setting them all to the same one... If so, the following is what you would probably rather do. # bar.length = size; // won't reallocate unless size is bigger than before # foreach (inout x; bar) # x = new T; Above all, have fun with your new D experience. :) -- Chris Nicholson-Sauls
Dec 13 2006
parent reply Sean Kelly <sean f4.ca> writes:
Chris Nicholson-Sauls wrote:
 
 That said, I find that I rarely miss container classes/structs in D.  
 The most common cases are handled by variable-length arrays.  (Although 
 I do have pseudo-member functions in Cashew for simulating a stack using 
 an array.)

I've got a module of array ops that offers heap and set operations, as well as sort, binary search, etc. And I feel quite the same. Sean
Dec 13 2006
parent reply Jason House <jhouse mitre.org> writes:
An earlier post showed stack with push and pop for an array.  How does one do a
queue? a set?

Also, I haven't found a way to check if something is in an associative array or
not.

This may all boil down to me not being able to find a comprehensive list of
array
properties and/or quality documentation on the D language.
Dec 14 2006
next sibling parent Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Jason House wrote:
 An earlier post showed stack with push and pop for an array.  How does one do a
 queue? a set?
 
 Also, I haven't found a way to check if something is in an associative array
or not.
 
 This may all boil down to me not being able to find a comprehensive list of
array
 properties and/or quality documentation on the D language.

Well... I don't know that I've ever done Queue/Set, but it wouldn't be hard. Quick cheap version of a Queue off the top of my head: # // really only neccessary for completeness # void qpush (T) (inout T[] haystack, T needle) { # haystack ~= needle; # } # # T qpop (T) (inout T[] haystack) { # T result = haystack[0]; # # haystack = haystack[1 .. $]; # return result; # } # # // and now in use # char[][] queue; # # queue.qpush("alpha"); # queue.qpush("beta"); # queue.qpush("gamma"); # # auto item = queue.qpop(); // item == "alpha" Or if one is using Cashew, you could really just do: # import cashew .utils .array ; # # alias push qpush ; # alias shift qpop ; As for a Set... well, that might take a class/struct wrapper to do succinctly. Or, again using cashew, you could just make a point of calling .unique() after each insert/concat. # int[] set = ... ; # # set ~= 42, set.unique(); Not very pretty though. -- Chris Nicholson-Sauls
Dec 14 2006
prev sibling next sibling parent Sean Kelly <sean f4.ca> writes:
Jason House wrote:
 An earlier post showed stack with push and pop for an array.  How does one do a
 queue? a set?

A queue isn't easy because array-based queues generally need to track where the head of the queue is (that or move the rest of the queue on each pop operation), so queues really need an object wrapper to be done well. However, storing the data in an array is still generally faster than using a linked list. I ran a test not too long ago in Java, and an array-based queue was roughly 50% faster than a linked list-based queue for inserting and removing one million elements, and that was using a 'for' loop to copy elements during a resize operation (instead of a memcpy). A free-list could improve average performance of a linked list implementation, but in a worst case scenario the array-based queue will still win (assuming a non-trivial allocation cost). Sets are easily modeled as sorted arrays however. But the real advantage of offering these algorithms apart from any specific container is that they can be applied to any sequence that supports the appropriate operations. For example, the set intersection operation could be applied to two SQL result sets as easily as to arrays, and no copying to a Set container would be necessary. My code currently only supports arrays, but that is mostly because there is no accepted iterator definition for D. But this topic was discussed pretty heavily about a month ago and I may go ahead and just create one. It's just lingering a bit low on my to-do list at the moment.
 Also, I haven't found a way to check if something is in an associative array
or not.

if( x in myAA ) {} The 'in' operator actually returns a pointer to the value, or null if the value is not present, so a test and modify operation might do: if( auto t = x in myAA ) *t = u;
 This may all boil down to me not being able to find a comprehensive list of
array
 properties and/or quality documentation on the D language.

The array documentation is actually some of the best in the spec. "in" is mentioned in the Associative Array section: http://www.digitalmars.com/d/arrays.html#associative But is also documented as an expression: http://www.digitalmars.com/d/expression.html#InExpression Sean
Dec 14 2006
prev sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Jason House" <jhouse mitre.org> wrote in message 
news:elsctg$5ca$1 digitaldaemon.com...
 An earlier post showed stack with push and pop for an array.  How does one 
 do a
 queue? a set?

You could also use an associative array to model a set. bool[int] set; set[4] = true; set[6] = true; set[10] = true; if(4 in set) writefln("4 is in the set"); foreach(v, nothing; set) writefln(v); set.remove(6); The "nothing" in the foreach loop is because the values in the associative array are useless -- they're just there because they have to be. In fact, why don't I just make a little set class to make it a little prettier. class Set(T) { protected bool[T] mData; public this() { } public void add(T value) { mData[value] = true; } public bool opIn_r(T value) { return ((value in mData) !is null); } public int opApply(int delegate(inout T value) dg) { int result = 0; foreach(value, n; mData) { result = dg(value); if(result) break; } return result; } public void remove(T value) { mData.remove(value); } } void main() { auto scope set = new Set!(int); set.add(4); set.add(6); set.add(10); if(4 in set) writefln("4 is in the set"); foreach(v; set) writefln(v); set.remove(6); }
Dec 14 2006
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Jarrett Billingsley wrote:
 "Jason House" <jhouse mitre.org> wrote in message 
 news:elsctg$5ca$1 digitaldaemon.com...
 An earlier post showed stack with push and pop for an array.  How does one 
 do a
 queue? a set?

You could also use an associative array to model a set.

That's the right way to go. Using arrays for sets is going to be sllloooooow.
 bool[int] set;
 set[4] = true;
 set[6] = true;
 set[10] = true;
 
 if(4 in set)
     writefln("4 is in the set");
 
 foreach(v, nothing; set)
     writefln(v);

Or just use foreach(v; set.keys) writefln(v);
 set.remove(6);
 
 The "nothing" in the foreach loop is because the values in the associative 
 array are useless -- they're just there because they have to be.
 
 In fact, why don't I just make a little set class to make it a little 
 prettier.

The only reason not would be because opApply is slower than the builtin foreach on an array. At least for regular arrays. Not sure if the same is true for AA's. Also does anyone know if .keys and .values are O(1) operations? Or do they have to allocate a new array and copy keys/values? That could be an important thing to know. Should be part of the spec, I think. --bb
Dec 14 2006
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Bill Baxter" <dnewsgroup billbaxter.com> wrote in message 
news:elsr1j$lu0$1 digitaldaemon.com...

 Or just use
 foreach(v; set.keys)
    writefln(v);

That prints "true true true". :)
 The only reason not would be because opApply is slower than the builtin 
 foreach on an array.  At least for regular arrays.  Not sure if the same 
 is true for AA's.

I think it'd be slower for AAs too because in the class opApply function you have to use foreach on the AA that holds the data. But really I guess it's only one method call, so it probably wouldn't be bad at all. After all, custom implementations of opApply work virtually the same way as the built-in ones.
 Also does anyone know if .keys and .values are O(1) operations?  Or do 
 they have to allocate a new array and copy keys/values?  That could be an 
 important thing to know.  Should be part of the spec, I think.

They allocate new arrays. But when you foreach an AA, it doesn't call them.
Dec 14 2006
next sibling parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote in message 
news:elsu1f$pq5$1 digitaldaemon.com...
 "Bill Baxter" <dnewsgroup billbaxter.com> wrote in message 
 news:elsr1j$lu0$1 digitaldaemon.com...

 Or just use
 foreach(v; set.keys)
    writefln(v);

That prints "true true true". :)

Whoops, nevermind, that's right. I missed the .keys.
Dec 14 2006
prev sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Jarrett Billingsley wrote:
 "Bill Baxter" <dnewsgroup billbaxter.com> wrote in message 
 news:elsr1j$lu0$1 digitaldaemon.com...
 
 Or just use
 foreach(v; set.keys)
    writefln(v);

That prints "true true true". :)

I don't think so. set.values would give you that.
 The only reason not would be because opApply is slower than the builtin 
 foreach on an array.  At least for regular arrays.  Not sure if the same 
 is true for AA's.

I think it'd be slower for AAs too because in the class opApply function you have to use foreach on the AA that holds the data. But really I guess it's only one method call, so it probably wouldn't be bad at all. After all, custom implementations of opApply work virtually the same way as the built-in ones.
 Also does anyone know if .keys and .values are O(1) operations?  Or do 
 they have to allocate a new array and copy keys/values?  That could be an 
 important thing to know.  Should be part of the spec, I think.

They allocate new arrays. But when you foreach an AA, it doesn't call them.

Hmm. Ok, so you really do want to use foreach(...; set) if at all possible. Sure would be nice if there were a .keyiter that could be used with foreach that was efficient and didn't require allocations. Too bad the iterator discussions fizzled out without anything getting decided. --bb
Dec 14 2006
next sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Bill Baxter" <dnewsgroup billbaxter.com> wrote in message 
news:elsv06$r2e$1 digitaldaemon.com...

 Hmm.  Ok, so you really do want to use foreach(...; set) if at all 
 possible.

 Sure would be nice if there were a .keyiter that could be used with 
 foreach that was efficient and didn't require allocations.

Meh, I don't think the foreach(key, ____; set) ... Is that bad. Maybe it's a little unclear, but as far as efficiency goes, it's just passing the extra unused value parameter.
 Too bad the iterator discussions fizzled out without anything getting 
 decided.

Yeah. We didn't start a three-week four-hundred-post thread about it, so I guess Walter didn't consider it important.
Dec 14 2006
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Jarrett Billingsley wrote:
 "Bill Baxter" <dnewsgroup billbaxter.com> wrote in message 
 news:elsv06$r2e$1 digitaldaemon.com...
 
 Hmm.  Ok, so you really do want to use foreach(...; set) if at all 
 possible.

 Sure would be nice if there were a .keyiter that could be used with 
 foreach that was efficient and didn't require allocations.

Meh, I don't think the foreach(key, ____; set) ... Is that bad. Maybe it's a little unclear, but as far as efficiency goes, it's just passing the extra unused value parameter.

I guess not. Might be interesting if AA's allowed a void value type. void[int] myset; Hmm... maybe not.
 
 Too bad the iterator discussions fizzled out without anything getting 
 decided.

Yeah. We didn't start a three-week four-hundred-post thread about it, so I guess Walter didn't consider it important.

:-) But it was almost that long wasn't it? And Walter was one of the the ones that kicked it off, IIRC. So I had high hopes something would come of it. --bb
Dec 14 2006
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Bill Baxter" <dnewsgroup billbaxter.com> wrote in message 
news:elsvmo$rsb$1 digitaldaemon.com...

 I guess not.  Might be interesting if AA's allowed a void value type.

   void[int] myset;

 Hmm... maybe not.

Actually, they used to, and people used them as sets :) Of course, that was also before accessing a nonexistent AA element was an indexing error, so to add something to the set, you did set[4] = set[4]; Which would add 4, or just be a no-op. Now that's just plain illegal, so even if void were allowed as the value type, there'd be no way to add anything to the set. Besides, what does void[int] mean? (then again you can have a void[], which is also kind of weird, but..).
 :-)  But it was almost that long wasn't it?  And Walter was one of the the 
 ones that kicked it off, IIRC.  So I had high hopes something would come 
 of it.

I don't know. Maybe we didn't say the right things.
Dec 14 2006
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
Bill Baxter wrote:
 
 Too bad the iterator discussions fizzled out without anything getting 
 decided.

I think the design was pretty well settled when things ended. The next step would be a sample implementation so folks could wrangle over syntax. It's on my "to do" list, but I'm a bit short on free time at the moment. Sean
Dec 14 2006
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Sean Kelly wrote:
 Bill Baxter wrote:
 Too bad the iterator discussions fizzled out without anything getting 
 decided.

I think the design was pretty well settled when things ended. The next step would be a sample implementation so folks could wrangle over syntax. It's on my "to do" list, but I'm a bit short on free time at the moment.

Huh. So what is your understanding of what the consensus was? --bb
Dec 14 2006
parent reply Sean Kelly <sean f4.ca> writes:
Bill Baxter wrote:
 Sean Kelly wrote:
 Bill Baxter wrote:
 Too bad the iterator discussions fizzled out without anything getting 
 decided.

I think the design was pretty well settled when things ended. The next step would be a sample implementation so folks could wrangle over syntax. It's on my "to do" list, but I'm a bit short on free time at the moment.

Huh. So what is your understanding of what the consensus was?

Java-style, with random access iterators overloading array operations. I can't remember if there was any clear preference for the hasNext/getNext vs. the atEnd/getVal approach, but I tend to favor the latter. Sean
Dec 15 2006
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Sean Kelly wrote:
 Bill Baxter wrote:
 Sean Kelly wrote:
 Bill Baxter wrote:
 Too bad the iterator discussions fizzled out without anything 
 getting decided.

I think the design was pretty well settled when things ended. The next step would be a sample implementation so folks could wrangle over syntax. It's on my "to do" list, but I'm a bit short on free time at the moment.

Huh. So what is your understanding of what the consensus was?

Java-style, with random access iterators overloading array operations. I can't remember if there was any clear preference for the hasNext/getNext vs. the atEnd/getVal approach, but I tend to favor the latter.

I like next/value/(ptr) myself. Makes for very succinct while loops. ptr available where it makes sense. while(iter.next()) { writefln(iter.value); } while(iter.next()) { *iter.ptr = 17; } --bb
Dec 15 2006
parent reply Sean Kelly <sean f4.ca> writes:
Bill Baxter wrote:
 Sean Kelly wrote:
 Bill Baxter wrote:
 Sean Kelly wrote:
 Bill Baxter wrote:
 Too bad the iterator discussions fizzled out without anything 
 getting decided.

I think the design was pretty well settled when things ended. The next step would be a sample implementation so folks could wrangle over syntax. It's on my "to do" list, but I'm a bit short on free time at the moment.

Huh. So what is your understanding of what the consensus was?

Java-style, with random access iterators overloading array operations. I can't remember if there was any clear preference for the hasNext/getNext vs. the atEnd/getVal approach, but I tend to favor the latter.

I like next/value/(ptr) myself. Makes for very succinct while loops. ptr available where it makes sense. while(iter.next()) { writefln(iter.value); } while(iter.next()) { *iter.ptr = 17; }

It's great for loops, but can be confusing when iterators stick around for a while. Say I have some code that operates on the current value of an iterator. With the next() approach, how do I know whether the iterator is valid? Still, perhaps a hybrid approach is best. Have next return true on success and offer an atEnd property as well. Sean
Dec 15 2006
parent reply Bill Baxter <wbaxter gmail.com> writes:
Sean Kelly wrote:
 Bill Baxter wrote:
 
 Sean Kelly wrote:

 Bill Baxter wrote:

 Sean Kelly wrote:

 Bill Baxter wrote:

 Too bad the iterator discussions fizzled out without anything 
 getting decided.

I think the design was pretty well settled when things ended. The next step would be a sample implementation so folks could wrangle over syntax. It's on my "to do" list, but I'm a bit short on free time at the moment.

Huh. So what is your understanding of what the consensus was?

Java-style, with random access iterators overloading array operations. I can't remember if there was any clear preference for the hasNext/getNext vs. the atEnd/getVal approach, but I tend to favor the latter.

I like next/value/(ptr) myself. Makes for very succinct while loops. ptr available where it makes sense. while(iter.next()) { writefln(iter.value); } while(iter.next()) { *iter.ptr = 17; }

It's great for loops, but can be confusing when iterators stick around for a while. Say I have some code that operates on the current value of an iterator. With the next() approach, how do I know whether the iterator is valid? Still, perhaps a hybrid approach is best. Have next return true on success and offer an atEnd property as well.

Good point. There's another problem related to iters sitting around a while, too, that I hadn't considered. If you have to call next() to get the first item then you'll also need some way to query if the iterator is in it's "pre-begin" state. So the next/atend approach only really works when there's no 'value' and 'next' is the only way to get the value. Ick. So maybe a for-ish way is better than while-ish way: for (; !iter.end; iter.next) { writefln(iter.value); } And have iterators start off valid (if not already at the end). --bb
Dec 15 2006
parent reply Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Bill Baxter wrote:
 Sean Kelly wrote:
 Bill Baxter wrote:

 Sean Kelly wrote:

 Bill Baxter wrote:

 Sean Kelly wrote:

 Bill Baxter wrote:

 Too bad the iterator discussions fizzled out without anything 
 getting decided.

I think the design was pretty well settled when things ended. The next step would be a sample implementation so folks could wrangle over syntax. It's on my "to do" list, but I'm a bit short on free time at the moment.

Huh. So what is your understanding of what the consensus was?

Java-style, with random access iterators overloading array operations. I can't remember if there was any clear preference for the hasNext/getNext vs. the atEnd/getVal approach, but I tend to favor the latter.




Yes, this was basically the consensus.
 I like next/value/(ptr) myself.
 Makes for very succinct while loops.  ptr available where it makes 
 sense.

 while(iter.next()) {  writefln(iter.value); }

 while(iter.next()) {  *iter.ptr = 17; }

It's great for loops, but can be confusing when iterators stick around for a while. Say I have some code that operates on the current value of an iterator. With the next() approach, how do I know whether the iterator is valid? Still, perhaps a hybrid approach is best. Have next return true on success and offer an atEnd property as well.

Good point. There's another problem related to iters sitting around a while, too, that I hadn't considered. If you have to call next() to get the first item then you'll also need some way to query if the iterator is in it's "pre-begin" state. So the next/atend approach only really works when there's no 'value' and 'next' is the only way to get the value. Ick. So maybe a for-ish way is better than while-ish way: for (; !iter.end; iter.next) { writefln(iter.value); } And have iterators start off valid (if not already at the end).

Yes, this may be better. This was basically the point the last iterator discussion reached. I would like to make this even more fun by throwing in bidirectional iterators. :) Just to clarify my terminology here, a forward iterator needs three things: 1(R) a way to reference the data 2(T) a way to traverse the data 3(S) a way to signal the end of the data If one wants an interface with less than 3 methods, the above behaviors need to be combined in some way. In news://news.digitalmars.com:119/eiqltc$22c1$1 digitaldaemon.com I briefly reviewd three different ways used by three different languages (Java, C#, Python). Java combines (R+T) through next() and (S) through hasNext() C# combines (T+S) through MoveNext() and (R) though Current() Python combines (R+T+S) in next() (throwing an exception for S) There are downsides with all approaches. C# requires the iterator to initially be in an invalid state. Java/Python disallows peek()ing without traversal. Python's exception requires more scaffolding at the use site. Now to bidirectional iterators. Separating R, T, S as three different methods, here are two ways a bidirectional iterator could be implemented: The value-style: BidirectionalIterator1(T) { T* begin; T* end; T* current; bool hasValue() { return cursor != end && cursor != (begin-1); } void moveNext() { current++; } void movePrevious() { current--; } T value() { return *current; } T value(T v) { return *current = v; } } Silly usage example: auto i = someIterator; while(i.hasValue) { if (i.value > x) i.moveNext; else i.movePrevious; } I like how succinct this turns out, but it has the disadvantage of internally needing two invalid values, one at the start and one at the end. Considering pointers internally, this means a pointer pointing before the start of an array, which is very bad. The other disadvantage is hasValue that has to check for both of these two cases. One alternative is the Java style version. Where the pointer/cursor lies conceptually between the elements instead of at an element, giving: BidirectionalIterator2(T) { T* begin; T* end; T* cursor; bool hasNext() { return cursor != end; } bool hasPrevious() { return cursor != begin; } void moveNext() { cursor++; } void movePrevious() { cursor--; } T next() { return *cursor; } T next(T v) { return *cursor = v; } T previous() { return *(cursor-1); } T previous(T v) { return *(cursor-1); } } auto i = someIterator; while(i.hasNext) { if (i.next > 3) i.moveNext; else i.movePrevious; } The disadvantage is requiring two additional methods (hasPrevious and previous), but the advantage is that it doesn't need more than 1 illegal value (pointing 1 past the end, which is well supported). Renaming next/previous into front/back or something may give more or less confusion. Probably the former. Apart from different method names, are there other variants? /Oskar
Dec 15 2006
parent Sean Kelly <sean f4.ca> writes:
Oskar Linde wrote:
 
 Now to bidirectional iterators.
 
 Separating R, T, S as three different methods, here are two ways a 
 bidirectional iterator could be implemented:
 
 The value-style:
 
 BidirectionalIterator1(T) {
     T* begin;
     T* end;
     T* current;
 
     bool hasValue() { return cursor != end && cursor != (begin-1); }
     void moveNext() { current++; }
     void movePrevious() { current--; }
 
     T value() { return *current; }
     T value(T v) { return *current = v; }
 }
 
 Silly usage example:
 
 auto i = someIterator;
 while(i.hasValue) {
     if (i.value > x)
         i.moveNext;
     else
         i.movePrevious;
 }
 
 I like how succinct this turns out, but it has the disadvantage of 
 internally needing two invalid values, one at the start and one at the 
 end.

Not necessarily. The dummy node could be shared by both. This is one reason why circular linked-list implementations are so common. Things do admittedly get a bit weird for binary trees, however.
 Considering pointers internally, this means a pointer pointing
 before the start of an array, which is very bad.

In C++, while a bidirectional iterator may be able to traverse in either direction, it still generally has some natural association as a forward or reverse iterator. That is, when the iterator is first returned from the sequence it was likely either initialized pointing to the first or last element. If it's the former then it's associated with forward iteration, if it's the latter then reverse iteration. For example, the C++ vector iterator is bidirectional (actually, random), but iterators returned by the begin() method are effectively forward iterators while iterators returned by the rbegin() method are effectively reverse iterators. Perhaps this is the correct approach to take here as well?
 The other disadvantage
 is hasValue that has to check for both of these two cases.

True enough. But range-checking is range-checking. If it's possible for an iterator to go out of range and it's possible to check for this condition then one should do so, at least in debug builds.
 One alternative is the Java style version. Where the pointer/cursor lies 
 conceptually between the elements instead of at an element, giving:
 
 BidirectionalIterator2(T) {
     T* begin;
     T* end;
     T* cursor;
 
     bool hasNext() { return cursor != end; }
     bool hasPrevious() { return cursor != begin; }
     void moveNext() { cursor++; }
     void movePrevious() { cursor--; }
 
     T next() { return *cursor; }
     T next(T v) { return *cursor = v; }
 
     T previous() { return *(cursor-1); }
     T previous(T v) { return *(cursor-1); }
 }
 
 
 auto i = someIterator;
 while(i.hasNext) {
     if (i.next > 3)
         i.moveNext;
     else
         i.movePrevious;
 }
     
 The disadvantage is requiring two additional methods (hasPrevious and 
 previous), but the advantage is that it doesn't need more than 1 illegal 
 value (pointing 1 past the end, which is well supported).

The other disadvantage is that this returns to the original problem where it's impossible to determine whether an iterator points to a valid location without moving.
 Apart from different method names, are there other variants?

See above. The C++ approach seems to make more sense to me, though I'm still mulling it over. There is a definite appeal to having iterators that are truly direction-agnostic, but as you've demonstrated above it does result in slightly weird implementation requirements. Since D is like C++ in that the one-past-the-end location is valid but one-past-the-beginning is not, it seems reasonable to preserve that behavior here, even if a more Java-like iterator design is used. Sean
Dec 27 2006
prev sibling parent Sean Kelly <sean f4.ca> writes:
Jason House wrote:
 Sean Kelly wrote:
 * Enums do not (and can not) have a toString property

They don't at the moment. One could be added, I imagine, but it would have to be done in the compiler.

That's exactly correct... Or at the very least the compiler must allow such properties to be added to them. I did try adding my own toString property (just in case there was neat hidden feature like that)

This actually works for arrays, but only arrays. People have asked in the past that it be extended to work for arbitrary types, but no luck so far. ie. void doSomething( char[] buf ); doSomething( "abc" ); "abc".doSomething(); Both of the above calls should work, since char[] is an array type. Sean
Dec 13 2006
prev sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Jason House wrote:
 I'm kind of doing a trial by fire method to learn D and how it works... I'm
 taking an existing open source project and converting it over.  Here's the
 stuff that I've found / questions that I have.

Great. Welcome.
 Thanks in advance for all the thoughtful responses.  I know this is a long
 post!  I've worked through the issues to get working code, but I think my
 issues will be hit by others.
 
 It seems that DTL and minTL [...]

Never tried either one, so can't help you there.
 I know of no way in d to provide a const qualifier.  It seems like the concept
 purely doesn't exist.  minTL appears to have a workaround where it templates
 itself on a boolean value, and simply doesn't publish certain functions.  When
 will a real const qualifier get added to the language?  It seems really
 strange that a language that adds all kinds of contract programming wouldn't
 use const qualifiers / const types.

That's right. D's const is limited to things whose value can be determined at compile time. So no const function parameters, for instance. I agree it is a bit odd. In particular I'd really like to have 'const inout' parameters for functions (though that actual nomenclature is nonsense). Right now declaring a parameter to be 'inout T' is the only way to make sure a huge value type T doesn't get copied on the stack. 'inout' is basically a T& in C++ terms. 'const T&' is probably more common in C++ than plain 'T&'. Sadly, D has no equivalent for it. My vote would be for an 'refin' or 'inref' parameter type in addition to the existing 'in', 'out', and 'inout' flavors. I think the reason D doesn't have it is just because C++ code gets pretty ugly with all those consts everywhere. Then there's const-correctness hell, when you decide to change one method to const and suddenly there's a ripple effect across your whole application. Until you get to the point where you discover that some 3rd party code out of your control is not const-correct and you ultimately resort to const_cast<>s. I guess I'm used to it, though. :-) That doesn't seem such a bad thing to me if it results in a program that more clearly documents which things can be modified. I'm certainly glad that D is not Fortran where every parameter is basically inout, and you have to read the documentation to see if what you pass in is going to be clobbered or not.
 After banging my head on printing stuff for a long time, I've come to a few
 conclusions:
 * The C++ equivalent to std::ostream& operator << (std::ostream &out, const
 type &x) does not exist

I think std.stream.OutputStream.write is the closest there is. But mainly you can't overload operators outside of classes in D. And also I think template lookup across modules doesn't work. What I recall being the killer the last time I brought up something like this was that Walter says it's too tricky to get Koenig lookup working. I'm not really sure what the deal is, but I think D is going to need it eventually. Correct me if I'm wrong, folks, but right now the only way to do things like C++'s operator<< (the compile-time polymorphism functionality, not the syntax) is to have an exhaustive list of static if, else static if, ... cases inside a template. Thus users can't add their own overrides in a different module.
 * Custom types use the toString property

Yes.
 * Enums do not (and can not) have a toString property

I guess not. But of course you can make a toString non-property for your enum. writefln(toString(myEnum)) instead of writefln(myEnum).
 * I have found no way to determine (in a templated class) if a type is
 printable or not.  static if(is(typeof(T))) comes close, but my very first
 test using ints failed.

You mean printable as in writefln(x) will work on it? Hmm. That is kinda tricky. I think there's a way to do it, but it will likely involve lots of static if's checking one-by-one for types that are printable. Ultimately, though, what's "printable" boils down to what things std.format.doFormat supports, and that's ultimately only determinable at runtime.
 The special unittest member function doesn't get built in with -unittest
 unless the actual class is used in some way (and then one copy of the unittest
 funciton gets run for each type).  I'd really like to see some way for this to
 work.  for example, class foo(T){ ... unittest{ foo!(int) sample; ...} } does
 not run unless other code instantiates foo!(U)

That sounds like a bug. I didn't even realize you could put unittests inside of classes. All the code I've seen has the unittests outside of the class.
 In my early efforts at doing operator overloading, I wrote opCmp and then was
 shocked when my unit test showed that == didn't even call opCmp.  It silently
 did something wrong.  This seems like a gotcha built into the language that
 should probably get remedied.

Like what? I'm not really to up on opEquals/opCmp. Should you get a warning if you only override opCmp? Or an error?
 I couldn't find a way to reinitialize a variable back to a default value...
 For example class foo(T){ T[] bar; void func(){bar[3] = new T();} } won't
 compile.  I'm not too sure why this is the case.

bar[3] = T.init is what you need for a value type. --bb
Dec 13 2006