www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Writing Bug-Free C/D Code

reply Knud Soerensen <4tuu4k002 sneakemail.com> writes:
This morning I stumbled over a on-line book named "Writing Bug-Free C Code"
http://www.duckware.com/bugfreec/index.html 
the gem of the book (chapter 4) is a way to implement an object model which
provide better data hiding than C++ classes.

It also gives advise like:
"Use a debugger only as a last resort. Having to resort to a debugger means
your programming methodologies have failed."
and 
"When fixing a bug, fix the underlying problem or cause of the bug and not just
the bug itself." 

D already have many feature which help the programmer make lesser
bugs, but how can we make it even better ?

I think a good place to start is look at the last bugs reported by the
users in released D programs, and consider if it is coursed by some
fundamental flaw in D and what can be done to catch it earlier in 
development.  

Maybe we need a place to report and discus this type of bugs.
Is the newsgroup okay or would a bug-tracking system be a better option ?
Mar 18 2007
next sibling parent reply Henning Hasemann <hhasemann web.de> writes:
I just start a few little things.

1.)
Most of the bugs I had when starting with D where that I simply forgot
to initialise members in the c'tor:

class Foo {
  void foo() { ... };
}

class Bar {
  Foo myFoo;
  void bar() {
    myFoo.foo();
  }
}

Of course, because here myFoo is default initialised to null this always gives
a segfault where myFoo is first being used in such a way,
so it is very easy to track down (if you use a debugger at least).

But sometimes I'd find it nice if there was a way to tell D:
Warn me at compile time if it is clear that some class instance members
will be null.

Of course you must be able to tell D exceptions to this, as you will want
to have a few members be null until later.

I have no good idea what this tool would syntactically look like or
if it would make sense to have it a compiler switch or whatever.
Probably the idea is almost bullsh*t nevertheless, because you get
used to initialise your members after a short while.

2.)
Another thing would be easy support for stack traced exceptions.
I tried flectioned last weak or so (just for this purpose) but it
didnt want to compile with my code (I think I was still on dmd-1.007 so
it might have been a bug there). I think STE's are of use
to many developers so they should be put into phobos or so.

3.)
Please change the implicit integral casting rules to be more c-like.
This has been discussed here and I cant remember a good argument
against casting only in such a way data doesnt get lost.
(ie: int->float is okay, but float->int, or int->uint is not)
I had errors that where very hard to track down, because somthing like
(-1 * 4u) yields the unsigned int representation of -4 which is
4294967292, which is rather unintuitive when there is no explicit cast.

I know, changing this might break some code, but I cant help considering
code that relies on such implicit casting rules broken nevertheless.

Henning

-- 
v4sw7Yhw4ln0pr7Ock2/3ma7uLw5Xm0l6/7DGKi2e6t6ELNSTVXb7AHIMOen5a2Xs5Mr2g5ACPR
hackerkey.com
Mar 18 2007
next sibling parent reply Thomas Kuehne <thomas-dloop kuehne.cn> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Henning Hasemann schrieb am 2007-03-19:

<snip>

 2.)
 Another thing would be easy support for stack traced exceptions.
 I tried flectioned last weak or so (just for this purpose) but it
 didnt want to compile with my code (I think I was still on dmd-1.007 so
 it might have been a bug there). I think STE's are of use
 to many developers so they should be put into phobos or so.
Exactly what problems did you encounter? Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFF/llXLK5blCcjpWoRAt7cAJ9+up6FUSdVXtLanAtmFFavSF5EaACdGqPO 7zoBbrnY8U2RTdSr+b2dPBA= =Iv+W -----END PGP SIGNATURE-----
Mar 19 2007
next sibling parent reply Henning Hasemann <hhasemann web.de> writes:
On Mon, 19 Mar 2007 08:41:48 +0000 (UTC)
Thomas Kuehne <thomas-dloop kuehne.cn> wrote:

 
 2.)
 Another thing would be easy support for stack traced exceptions.
 I tried flectioned last weak or so (just for this purpose) but it
 didnt want to compile with my code (I think I was still on dmd-1.007 so
 it might have been a bug there). I think STE's are of use
 to many developers so they should be put into phobos or so.
Exactly what problems did you encounter?
It does not correctly recognise that I'm on phobos I guess because when compiling I get: flectioned.d(78): module Vararg cannot read file 'tango/core/Vararg.d' I use dmd-1.009 and rebuild-0.12 for compiling. OS is linux. dmd -c flectioned.d runs fine, so maybe rebuild is the real problem? Henning -- v4sw7Yhw4ln0pr7Ock2/3ma7uLw5Xm0l6/7DGKi2e6t6ELNSTVXb7AHIMOen5a2Xs5Mr2g5ACPR hackerkey.com
Mar 19 2007
next sibling parent reply Thomas Kuehne <thomas-dloop kuehne.cn> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Henning Hasemann schrieb am 2007-03-19:
 On Mon, 19 Mar 2007 08:41:48 +0000 (UTC)
 Thomas Kuehne <thomas-dloop kuehne.cn> wrote:

 
 2.)
 Another thing would be easy support for stack traced exceptions.
 I tried flectioned last weak or so (just for this purpose) but it
 didnt want to compile with my code (I think I was still on dmd-1.007 so
 it might have been a bug there). I think STE's are of use
 to many developers so they should be put into phobos or so.
Exactly what problems did you encounter?
It does not correctly recognise that I'm on phobos I guess because when compiling I get: flectioned.d(78): module Vararg cannot read file 'tango/core/Vararg.d' I use dmd-1.009 and rebuild-0.12 for compiling. OS is linux. dmd -c flectioned.d runs fine, so maybe rebuild is the real problem?
Do you use the original Phobos? Line 65 checks if Object.toUtf8 is defined wich would indicate that Tango is installed. Failing to import tango.core.Vararg however indicates that Tango isn't installed. Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFF/ocSLK5blCcjpWoRAsoEAJwM1dz8BdczPa1pM++8Trx/bWpipQCgpfpx z8H3iAh1LAKbRrsKiF8BJxE= =eoj2 -----END PGP SIGNATURE-----
Mar 19 2007
next sibling parent Henning Hasemann <hhasemann web.de> writes:
On Mon, 19 Mar 2007 11:58:15 +0000 (UTC)
Thomas Kuehne <thomas-dloop kuehne.cn> wrote:

 Do you use the original Phobos?
 
 Line 65 checks if Object.toUtf8 is defined wich would indicate that
 Tango is installed. Failing to import tango.core.Vararg however
 indicates that Tango isn't installed.
Everything thats installed is from the original dmd.zip from digitalmars.com, I never had tango installed nor a non-standard phobos version or something. I dont get it seems my rebuild is somehow messed up. Strange it compiles my phobos code without complains (and the code uses toString etc...) Henning -- v4sw7Yhw4ln0pr7Ock2/3ma7uLw5Xm0l6/7DGKi2e6t6ELNSTVXb7AHIMOen5a2Xs5Mr2g5ACPR hackerkey.com
Mar 19 2007
prev sibling parent =?ISO-8859-1?Q?Lu=EDs_Marques?= <luismarques gmail.com> writes:
I also had problems with Flectioned and DMD 1.007.
Please see the post on http://www.dsource.org/forums/viewtopic.php?t=2512
from March 6.

Luís

Thomas Kuehne wrote:
 -----BEGIN PGP SIGNED MESSAGE-----
 Hash: SHA1
 
 Henning Hasemann schrieb am 2007-03-19:
 On Mon, 19 Mar 2007 08:41:48 +0000 (UTC)
 Thomas Kuehne <thomas-dloop kuehne.cn> wrote:

 2.)
 Another thing would be easy support for stack traced exceptions.
 I tried flectioned last weak or so (just for this purpose) but it
 didnt want to compile with my code (I think I was still on dmd-1.007 so
 it might have been a bug there). I think STE's are of use
 to many developers so they should be put into phobos or so.
Exactly what problems did you encounter?
It does not correctly recognise that I'm on phobos I guess because when compiling I get: flectioned.d(78): module Vararg cannot read file 'tango/core/Vararg.d' I use dmd-1.009 and rebuild-0.12 for compiling. OS is linux. dmd -c flectioned.d runs fine, so maybe rebuild is the real problem?
Do you use the original Phobos? Line 65 checks if Object.toUtf8 is defined wich would indicate that Tango is installed. Failing to import tango.core.Vararg however indicates that Tango isn't installed. Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFF/ocSLK5blCcjpWoRAsoEAJwM1dz8BdczPa1pM++8Trx/bWpipQCgpfpx z8H3iAh1LAKbRrsKiF8BJxE= =eoj2 -----END PGP SIGNATURE-----
Mar 19 2007
prev sibling parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Henning Hasemann wrote:
 On Mon, 19 Mar 2007 08:41:48 +0000 (UTC)
 Thomas Kuehne <thomas-dloop kuehne.cn> wrote:
 
 2.)
 Another thing would be easy support for stack traced exceptions.
 I tried flectioned last weak or so (just for this purpose) but it
 didnt want to compile with my code (I think I was still on dmd-1.007 so
 it might have been a bug there). I think STE's are of use
 to many developers so they should be put into phobos or so.
Exactly what problems did you encounter?
It does not correctly recognise that I'm on phobos I guess because when compiling I get: flectioned.d(78): module Vararg cannot read file 'tango/core/Vararg.d' I use dmd-1.009 and rebuild-0.12 for compiling. OS is linux. dmd -c flectioned.d runs fine, so maybe rebuild is the real problem?
That module should only be imported if Object has a method toUtf8(), which in Phobos it doesn't. Check your import paths, and make sure you're using the right object.d[i].
Mar 19 2007
prev sibling parent Sean Kelly <sean f4.ca> writes:
Thomas Kuehne wrote:
 -----BEGIN PGP SIGNED MESSAGE-----
 Hash: SHA1
 
 Henning Hasemann schrieb am 2007-03-19:
 
 <snip>
 
 2.)
 Another thing would be easy support for stack traced exceptions.
 I tried flectioned last weak or so (just for this purpose) but it
 didnt want to compile with my code (I think I was still on dmd-1.007 so
 it might have been a bug there). I think STE's are of use
 to many developers so they should be put into phobos or so.
Exactly what problems did you encounter?
Here's one I got testing flectioned with Tango. Notice that fnB is missing from the trace: C:\code\src\d\test\flectioned>test Uh-oh! ---------------- 0x0012FE24 0x004020BA D4glue12traceHandlerFPvZC5tango4core9Exception19 TracedExceptionInfo15FlectionedTrace5_ctorMFPvZC4glue12traceHandlerFPvZC5tango4c ore9Exception19TracedExceptionInfo15FlectionedTrace 0x0012FE40 0x00402081 object.ClassInfo glue.traceHandler(void*) 0x0012FE64 0x00409068 object.ClassInfo tango.core.Exception.traceConte xt(void*) 0x0012FE6C 0x00402018 void test.fnA() 0x0012FE74 0x00402054 int main(char[][]) C:\code\src\d\test\flectioned>type test.d import tango.core.Exception; import glue; void fnA() { fnB(); } void fnB() { throw new TracedException( "Uh-oh!" ); } void main() { fnA(); } C:\code\src\d\test\flectioned>
Mar 19 2007
prev sibling next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Henning Hasemann wrote:
 I just start a few little things.
 
 1.)
 Most of the bugs I had when starting with D where that I simply forgot
 to initialise members in the c'tor:
 
 class Foo {
   void foo() { ... };
 }
 
 class Bar {
   Foo myFoo;
   void bar() {
     myFoo.foo();
   }
 }
 
 Of course, because here myFoo is default initialised to null this always gives
 a segfault where myFoo is first being used in such a way,
 so it is very easy to track down (if you use a debugger at least).
 
 But sometimes I'd find it nice if there was a way to tell D:
 Warn me at compile time if it is clear that some class instance members
 will be null.
 
 Of course you must be able to tell D exceptions to this, as you will want
 to have a few members be null until later.
 
 I have no good idea what this tool would syntactically look like or
 if it would make sense to have it a compiler switch or whatever.
 Probably the idea is almost bullsh*t nevertheless, because you get
 used to initialise your members after a short while.
You do get used to it, and you're right: how exactly could a tool tell the difference between "accidentally null" and "deliberately null"? I've found, actually, that with type inference I almost never get this problem. Nowadays, I instinctively write the above as "auto myFoo = new Foo;". If I get to the end of the line, and haven't actually mentioned a type anywhere, that's a good signal I've forgotten something :P It also helps that you can't declare an auto variable without initialising it.
 2.)
 Another thing would be easy support for stack traced exceptions.
 I tried flectioned last weak or so (just for this purpose) but it
 didnt want to compile with my code (I think I was still on dmd-1.007 so
 it might have been a bug there). I think STE's are of use
 to many developers so they should be put into phobos or so.
I agree that more informative exceptions would be fantastic.
 3.)
 Please change the implicit integral casting rules to be more c-like.
 This has been discussed here and I cant remember a good argument
 against casting only in such a way data doesnt get lost.
 (ie: int->float is okay, but float->int, or int->uint is not)
 I had errors that where very hard to track down, because somthing like
 (-1 * 4u) yields the unsigned int representation of -4 which is
 4294967292, which is rather unintuitive when there is no explicit cast.
It's unintuitive because you're asking the compiler to perform a nonsensical operation: multiplication between two partially disjoint sets. It's a bit like complaining that (3 * "2") doesn't work in D. In PHP, it results in 6, in Python, it results in "222". Which one's right?
 I know, changing this might break some code, but I cant help considering
 code that relies on such implicit casting rules broken nevertheless.
This is, in my opinion, a really bad idea. You're basically saying that the compiler should never complain if an operation *destroys data*. What's more, it's these sorts of destructive implicit casts that are very difficult to track down. The root problem here is that you're trying to mix signed and unsigned operations together, and expecting a signed result. My personal rule of thumb is: never, ever get signed/unsigned types within five kilometres of each other. If you think something is signed, but aren't sure, assert it! I actually think a better solution would be to add an Integer type to D: one that actually, really, truly works like an integer, and not a member of Z_{2^32} :P
 Henning
 
-- Daniel -- Unlike Knuth, I have neither proven or tried the above; it may not even make sense. v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
Mar 19 2007
parent reply Henning Hasemann <hhasemann web.de> writes:
On Mon, 19 Mar 2007 19:46:50 +1100
Daniel Keep <daniel.keep.lists gmail.com> wrote:

 It's unintuitive because you're asking the compiler to perform a
 nonsensical operation: multiplication between two partially disjoint sets.
You are right with this: Neither int nor uint are a good default type to convert to here. But when I compile with -w where I get a warning when putting an int into a short, I want to somehow get warned here that the meaning of the - sign may get reinterpreted, or better said: That the result of this operation is implicitly casted to a type which is not capable of representing it without loss of data or in this case meaning (as actually no bit gets lost). This can be very hard to track down if you have such things with variables. Say you are mocking around with SDL and take a width of some thing and calculating arounds with ints (width * negative value *can* be a useful value). If your calculation is long/complex enough it'll take some time finding this.
 It's a bit like complaining that (3 * "2") doesn't work in D.  In PHP,
 it results in 6, in Python, it results in "222".  Which one's right?
Yeah right, but as you mentioned, D doesnt *allow* this instead of returning one of the questionable results.
 I know, changing this might break some code, but I cant help considering
 code that relies on such implicit casting rules broken nevertheless.
This is, in my opinion, a really bad idea. You're basically saying that the compiler should never complain if an operation *destroys data*.
Now. I say it should either implicitly cast so it doesnt destroy data (ie float * int = float etc...), or complain when it has to destroy data. In the example -2 * 3u at least meaning is changed without any complaint. I *do not* say this should be an int. This should be either implicicly a long (which would be capable of representing all results from int * uint) or at least some warning when compiling with -w. But I must confess, after writing all the things above I tested around a bit again and saw: Implicit conversions other than the int * uint case, such as int * float do what I would expect. The int * uint thing got me so screwed in the past I casted everything manually because I feared D could do something evil ,-)
 The root problem here is that you're trying to mix signed and unsigned
 operations together, and expecting a signed result.  My personal rule of
 thumb is: never, ever get signed/unsigned types within five kilometres
 of each other.  If you think something is signed, but aren't sure,
 assert it!
I sometimes have some problems with this, as said before: Let SDL return a width as uint and have your positions int (because they also can be negative). I had this problems with C already. Examples of questions I have often when coding: - "Hmm okay. You're a on-screen coordinate. You will be positive. But I might to want to mix you with virtual and potetially negative Coordinates. Also you wont be larger than 32000, as long as screens wont get really big. But I somehow feel I should make you an int..." - Hm okay so I have these few things I know of they wont be negative. Should I make them int nevertheless? Would avoid complications and warinings, and the value range wouldnt be a problem.
 I actually think a better solution would be to add an Integer type to D:
 one that actually, really, truly works like an integer, and not a member
 of Z_{2^32} :P
Yeah, like the python one. Should not be too hard to implement I guess. Henning -- v4sw7Yhw4ln0pr7Ock2/3ma7uLw5Xm0l6/7DGKi2e6t6ELNSTVXb7AHIMOen5a2Xs5Mr2g5ACPR hackerkey.com
Mar 19 2007
next sibling parent Daniel Keep <daniel.keep.lists gmail.com> writes:
Henning Hasemann wrote:
 On Mon, 19 Mar 2007 19:46:50 +1100
 Daniel Keep <daniel.keep.lists gmail.com> wrote:
 
 It's unintuitive because you're asking the compiler to perform a
 nonsensical operation: multiplication between two partially disjoint sets.
You are right with this: Neither int nor uint are a good default type to convert to here. But when I compile with -w where I get a warning when putting an int into a short, I want to somehow get warned here that the meaning of the - sign may get reinterpreted, or better said: That the result of this operation is implicitly casted to a type which is not capable of representing it without loss of data or in this case meaning (as actually no bit gets lost). This can be very hard to track down if you have such things with variables. Say you are mocking around with SDL and take a width of some thing and calculating arounds with ints (width * negative value *can* be a useful value). If your calculation is long/complex enough it'll take some time finding this.
 It's a bit like complaining that (3 * "2") doesn't work in D.  In PHP,
 it results in 6, in Python, it results in "222".  Which one's right?
Yeah right, but as you mentioned, D doesnt *allow* this instead of returning one of the questionable results.
 I know, changing this might break some code, but I cant help considering
 code that relies on such implicit casting rules broken nevertheless.
This is, in my opinion, a really bad idea. You're basically saying that the compiler should never complain if an operation *destroys data*.
Now. I say it should either implicitly cast so it doesnt destroy data (ie float * int = float etc...), or complain when it has to destroy data. In the example -2 * 3u at least meaning is changed without any complaint. I *do not* say this should be an int. This should be either implicicly a long (which would be capable of representing all results from int * uint) or at least some warning when compiling with -w.
I like both of those ideas :)
 But I must confess, after writing all the things above I tested around
 a bit again and saw: Implicit conversions other than the int * uint case,
 such as int * float do what I would expect.
 The int * uint thing got me so screwed in the past I casted everything
 manually because I feared D could do something evil ,-)
 
 
 The root problem here is that you're trying to mix signed and unsigned
 operations together, and expecting a signed result.  My personal rule of
 thumb is: never, ever get signed/unsigned types within five kilometres
 of each other.  If you think something is signed, but aren't sure,
 assert it!
I sometimes have some problems with this, as said before: Let SDL return a width as uint and have your positions int (because they also can be negative). I had this problems with C already. Examples of questions I have often when coding: - "Hmm okay. You're a on-screen coordinate. You will be positive. But I might to want to mix you with virtual and potetially negative Coordinates. Also you wont be larger than 32000, as long as screens wont get really big. But I somehow feel I should make you an int..." - Hm okay so I have these few things I know of they wont be negative. Should I make them int nevertheless? Would avoid complications and warinings, and the value range wouldnt be a problem.
 I actually think a better solution would be to add an Integer type to D:
 one that actually, really, truly works like an integer, and not a member
 of Z_{2^32} :P
Yeah, like the python one. Should not be too hard to implement I guess. Henning
Thanks to being able to overload opAssign, this should be possible to do in a fairly sane way now. I wonder what the performance implications would be, though... I suppose it can't be any worse than Python's long type :P The other idea I had ages ago was that it would be great if compilers actually gave us distinct native types for integers (which throw an exception if they over/underflow) and modulo integers (which wrap around quite happily). -- Daniel -- Unlike Knuth, I have neither proven or tried the above; it may not even make sense. v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
Mar 19 2007
prev sibling parent reply Knud Soerensen <4tuu4k002 sneakemail.com> writes:
On Mon, 19 Mar 2007 11:44:54 +0100, Henning Hasemann wrote:

 
 I sometimes have some problems with this, as said before: Let SDL return
 a width as uint and have your positions int (because they also can be
negative).
 
 I had this problems with C already. Examples of questions I have often when
coding:
 - "Hmm okay. You're a on-screen coordinate. You will be positive.
   But I might to want to mix you with virtual and potetially negative
   Coordinates. Also you wont be larger than 32000, as long as screens
   wont get really big. But I somehow feel I should make you an int..."
 - Hm okay so I have these few things I know of they wont be negative.
   Should I make them int nevertheless? Would avoid complications and warinings,
   and the value range wouldnt be a problem.
Well, I think your problems is due to fundamental errors in your programming method. :-) We all know the ordinary form of Hungarian notation as described in http://www.duckware.com/bugfreec/chapter3.html#naming but there is also a stronger form called app-Hungarian notation where the prefix gives a hint of the purpose for the variable. Like rowPos for a row position and colPos for a column position. Now it is easy to spot buggy code like rowPos=colPos etc. Using a type system we can take the idea a step further. typedef int ROWPOS; typedef int COLPOS; foo { ROWPOS rowPos; COLPOS colPos; rowPos = colPos; // error at compiler time. } So, the fundamental flaw is that you use raw types instead of making a type for each purpose in your code. Could the compiler do something about it ? Yes, it could provide a switch called -bugfree which trows a error every time a non user defined type is used in the code. It would also be useful if we could add an invariance to the type instead of have to change it into a class or structure.
Mar 19 2007
next sibling parent reply Henning Hasemann <hhasemann web.de> writes:
On Mon, 19 Mar 2007 14:20:31 +0000 (UTC)
Knud Soerensen <4tuu4k002 sneakemail.com> wrote:

 Well, I think your problems is due to fundamental errors in your
 programming method. :-)
 
 We all know the ordinary form of Hungarian notation as described in
 http://www.duckware.com/bugfreec/chapter3.html#naming
 but there is also a stronger form called app-Hungarian notation
 where the prefix gives a hint of the purpose for the variable.
 Like rowPos for a row position and colPos for a column position.
 Now it is easy to spot buggy code like rowPos=colPos etc.
I always name my variables like this. Personally I event tend to not use such shortcut-names as they sometimes tend do be misleading and hard to read. (row is clear, but later you might come to a point where you or someone in your project uses column because he thinks its short enough and so on). Also this has nothing to do with my problems, because my problem is not that I adding things together that have nothing to do with each other, but adding things together that *have* to do with each other but are often of different signedness.
 Using a type system we can take the idea a step further.
 
 typedef int ROWPOS;
 typedef int COLPOS;
 
 foo 
 {
   ROWPOS rowPos;
   COLPOS colPos;
  
   rowPos = colPos; // error at compiler time.
 }
Yeah sometimes I tend to this too, but not for preventig errors like rowPos = colPos (as the naming you suggested helps me enough). But I also try to be careful not to have too much typedefs and/or aliases (yes I know the difference) to trivial types such as int as it might confuse a reader that assumes something special or magical beyond this.
 So, the fundamental flaw is that you use raw types instead
 of making a type for each purpose in your code.
Are you really sure it is a good idea to have a typedef for each purpose? So a point struct would look like this for you: typedef int COORD; struct Point { COORD x, y; } right? Dont get me wrong, I dont want to criticize this way of doing things its just I never done it so much before because I am not sure it is such a good idea.
 Could the compiler do something about it ?
 Yes, it could provide a switch called -bugfree 
 which trows a error every time a non user defined type is
 used in the code.
C'mon, that would be silly.
 It would also be useful if we could add an invariance to the type
 instead of have to change it into a class or structure.
You mean something like typedef ubyte DiceResult; DiceResult.invariant { assert(1 <= value); assert(value <= 6); } ... would be handy sometimes, I agree. Henning -- v4sw7Yhw4ln0pr7Ock2/3ma7uLw5Xm0l6/7DGKi2e6t6ELNSTVXb7AHIMOen5a2Xs5Mr2g5ACPR hackerkey.com
Mar 19 2007
next sibling parent Knud Soerensen <4tuu4k002 sneakemail.com> writes:
On Mon, 19 Mar 2007 16:02:04 +0100, Henning Hasemann wrote:

 On Mon, 19 Mar 2007 14:20:31 +0000 (UTC)
 Knud Soerensen <4tuu4k002 sneakemail.com> wrote:
 
 Well, I think your problems is due to fundamental errors in your
 programming method. :-)
 
 We all know the ordinary form of Hungarian notation as described in
 http://www.duckware.com/bugfreec/chapter3.html#naming
 but there is also a stronger form called app-Hungarian notation
 where the prefix gives a hint of the purpose for the variable.
 Like rowPos for a row position and colPos for a column position.
 Now it is easy to spot buggy code like rowPos=colPos etc.
I always name my variables like this. Personally I event tend to not use such shortcut-names as they sometimes tend do be misleading and hard to read. (row is clear, but later you might come to a point where you or someone in your project uses column because he thinks its short enough and so on). Also this has nothing to do with my problems, because my problem is not that I adding things together that have nothing to do with each other, but adding things together that *have* to do with each other but are often of different signedness.
well in your talk about adding a unsigned position with a signed width to get a new position. Idealy I would do typedef int WIDTH; typedef int POSITION; POSITION.invariant { assert(0 <= value); } void main() { POSITION pos=10; WIDTH wid=-2; pos=cast(POSITION)(pos+wid); } First it encapsulates the problem in the typedef, the cast help to make it clear what is happening and invariant allow us to test for runtime errors.
 Using a type system we can take the idea a step further.
 
 typedef int ROWPOS;
 typedef int COLPOS;
 
 foo 
 {
   ROWPOS rowPos;
   COLPOS colPos;
  
   rowPos = colPos; // error at compiler time.
 }
Yeah sometimes I tend to this too, but not for preventig errors like rowPos = colPos (as the naming you suggested helps me enough). But I also try to be careful not to have too much typedefs and/or aliases (yes I know the difference) to trivial types such as int as it might confuse a reader that assumes something special or magical beyond this.
Well, it can also be used as a help: typedef float RADIANS; main { 2dshape box; ... box.rotate(cast(RADIANS)1.54); } Here it makes the code more readable and less confusing.
 So, the fundamental flaw is that you use raw types instead
 of making a type for each purpose in your code.
Remember we are talking about writing complete bug free programs here. That means that we ideally should be able to catch every conceivable bug early.
 Are you really sure it is a good idea to have a typedef for each
 purpose? So a point struct would look like this for you:
 
 typedef int COORD;
 struct Point {
   COORD x, y;
 }
 
 right?
No, like. typedef int XCOORD; typedef int YCOORD; struct Point { XCOORD x; YCOORD y; } Or else your might get x and y confused :-)
 
 Dont get me wrong, I dont want to criticize this way of doing things
 its just I never done it so much before because I am not sure
 it is such a good idea.
 
 Could the compiler do something about it ?
 Yes, it could provide a switch called -bugfree 
 which trows a error every time a non user defined type is
 used in the code.
C'mon, that would be silly.
Well the idea with the -bugfree switch was to be able to catch every conceivable bug at compiler time, not to go easy on the programmer :-)
 
 It would also be useful if we could add an invariance to the type
 instead of have to change it into a class or structure.
You mean something like typedef ubyte DiceResult; DiceResult.invariant { assert(1 <= value); assert(value <= 6); }
Exactly.
 ... would be handy sometimes, I agree. 
 
 Henning
Mar 19 2007
prev sibling next sibling parent reply "David B. Held" <dheld codelogicconsulting.com> writes:
Henning Hasemann wrote:
 [...]
 Are you really sure it is a good idea to have a typedef for each purpose?
 So a point struct would look like this for you:
 
 typedef int COORD;
 struct Point {
   COORD x, y;
 }
 
 right?
 [...]
Absolutely. 90% of what programmers call builtin "types" are really *representations*. That is, they are classifications of bit patterns that have nothing to do with "types". In fact, if it's builtin, you can be pretty much guaranteed it's not a type. For instance, "int" is only a type when every value that an int can take on has a semantically valid meaning for that variable, *and* when those values can be mixed with any other instance of "int" in a meaningful way. In the vast majority of cases, that's not true. Of course, the reason most people don't make proper types is A) they are lazy, and B) it implies additional cost. However, as programs become bigger and correctness becomes more difficult to prove, using proper types becomes more important. It's not easy to illustrate in a 100 line program, but it becomes perfectly obvious in a 100,000 line codebase. Dave
Mar 19 2007
parent Daniel Keep <daniel.keep.lists gmail.com> writes:
David B. Held wrote:
 Henning Hasemann wrote:
 [...]
 Are you really sure it is a good idea to have a typedef for each purpose?
 So a point struct would look like this for you:

 typedef int COORD;
 struct Point {
   COORD x, y;
 }

 right?
 [...]
Absolutely. 90% of what programmers call builtin "types" are really *representations*. That is, they are classifications of bit patterns that have nothing to do with "types". In fact, if it's builtin, you can be pretty much guaranteed it's not a type. For instance, "int" is only a type when every value that an int can take on has a semantically valid meaning for that variable, *and* when those values can be mixed with any other instance of "int" in a meaningful way. In the vast majority of cases, that's not true. Of course, the reason most people don't make proper types is A) they are lazy, and B) it implies additional cost. However, as programs become bigger and correctness becomes more difficult to prove, using proper types becomes more important. It's not easy to illustrate in a 100 line program, but it becomes perfectly obvious in a 100,000 line codebase. Dave
I agree with you; for instance, in my research project, I made heaps of typedefs to ensure that I never accidentally got my wires crossed. The *massive* problem with this, however, is that it breaks *everything*. You can't toString a typedefed type, you can't use writefln with it... you've basically got to start from scratch again. Which is a massive pain if you don't have access to the source :P You end up just casting them back to their base type all the time, which kinda defeats the purpose of having them. Eventually, I ended up using structs for all of my "typedefs" because that way I can actually make them useful. It would be a *god send* if we could get type extensions. -- Daniel -- int getRandomNumber() { return 4; // chosen by fair dice roll. // guaranteed to be random. } v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
Mar 19 2007
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
Henning Hasemann wrote:
 
 Are you really sure it is a good idea to have a typedef for each purpose?
Maybe not a typedef for each purpose, but more generally, a type for each concept. I'm currently reading "Prefactoring" by Ken Pugh and he distills it nicely by saying "don't throw away information." Most data have associated constraints, etc, and even if this doesn't seem important at the outset, it's far easier to refine an existing type later on than it is to try and figure out which instances of 'int' in your application represent a person's age, for example. Sean
Mar 19 2007
parent Manfred Nowak <svv1999 hotmail.com> writes:
Sean Kelly wrote

 he distills it nicely by saying "don't throw away information."
 Most data have associated constraints,
Coincidentally I stumbled upon the fact, that statements may also carry information with them, which gets lost on resorting to the keywords provided by the given language. I first noticed that when reading about the twixt-keyword of the nickle system http://nickle.org/tutorial/x671.html#AEN706 which gives the generic if-keyword an additional meaning. After this detection my mental habit to resort conditions to the if- keyword started to crumble. Manfred_Nowak
Mar 19 2007
prev sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Mon, 19 Mar 2007 14:20:31 +0000 (UTC), Knud Soerensen wrote:

 Using a type system we can take the idea a step further.
 
 typedef int ROWPOS;
 typedef int COLPOS;
 
 foo 
 {
   ROWPOS rowPos;
   COLPOS colPos;
  
   rowPos = colPos; // error at compiler time.
 }
 
Currently D doesn't seem to support typedefs (user defined types) as cleanly as I'd hope it would. ------------ import std.stdio; typedef float age; typedef float len; void main() { age x; len y; float z = 4.321; x = 10; y = 5; //y = z; // fails but probably shouldn't //y = x; // correctly fails. y += x; // incorrectly works. writefln("x=%s y=%s", x, y); } ------------ -- Derek (skype: derek.j.parnell) Melbourne, Australia "Justice for David Hicks!" 20/03/2007 5:05:40 PM
Mar 19 2007
parent reply "David B. Held" <dheld codelogicconsulting.com> writes:
Derek Parnell wrote:
 [...]
  void main()
  {
    age x;
    len y;
    float z = 4.321;
 
    x = 10;
    y = 5;
    //y = z; // fails but probably shouldn't
I disagree. Consider: int x = 15; months m = x; // Sound?
    //y = x; // correctly fails.
    y += x;  // incorrectly works.
 [...]
Ouch. Dave
Mar 20 2007
parent reply Derek Parnell <derek psych.ward> writes:
On Tue, 20 Mar 2007 00:18:24 -0800, David B. Held wrote:

 Derek Parnell wrote:
 [...]
  void main()
  {
    age x;
    len y;
    float z = 4.321;
 
    x = 10;
    y = 5;
    //y = z; // fails but probably shouldn't
I disagree. Consider: int x = 15; months m = x; // Sound?
Of course it is. Unless you are talking about months-per-year, but that's not 'months' is it? The trouble is that D's typedef does not allow you to extend the semantics of the base type, but that's what structs and classes are for, no? The Euphoria language has a slightly different type construction mechanism which has its good side and bad side, but maybe something like it could be used to extend D's typedef mechanism. type month(object x) if not integer(x) then return 0 end if if x < 1 then return 0 end if if x > 12 then return 0 end if return 1 end type month m m = 15 -- FAILS with a type mismatch error. -- Derek Parnell Melbourne, Australia "Justice for David Hicks!" skype: derek.j.parnell
Mar 20 2007
parent reply "David B. Held" <dheld codelogicconsulting.com> writes:
Derek Parnell wrote:
 [...]
 The trouble is that D's typedef does not allow you to extend the semantics
 of the base type, but that's what structs and classes are for, no?
 
 The Euphoria language has a slightly different type construction mechanism
 which has its good side and bad side, but maybe something like it could be
 used to extend D's typedef mechanism.
 [...]
My understanding is that Contracts in D allow you to implement Ada-like SubRanges. This is actually one of the things I really liked about the DbC support. Is that not the case? Dave
Mar 21 2007
parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Wed, 21 Mar 2007 00:35:46 -0800, David B. Held wrote:

 Derek Parnell wrote:
 [...]
 The trouble is that D's typedef does not allow you to extend the semantics
 of the base type, but that's what structs and classes are for, no?
 
 The Euphoria language has a slightly different type construction mechanism
 which has its good side and bad side, but maybe something like it could be
 used to extend D's typedef mechanism.
 [...]
My understanding is that Contracts in D allow you to implement Ada-like SubRanges. This is actually one of the things I really liked about the DbC support. Is that not the case?
Contracts are optional. That is, if something is compiled with the -release switch, then contract code is not inserted into the executable. You cannot, and must not, rely on contract code for 'production' editions of software. It is only meant to be used during the development phases of an application, thus you should not use contracts to try and detect invalid input data for a function. Contract code should be used to detect logic errors and not data errors. -- Derek (skype: derek.j.parnell) Melbourne, Australia "Justice for David Hicks!" 21/03/2007 6:41:57 PM
Mar 21 2007
next sibling parent reply Dan <murpsoft hotmail.com> writes:
 It is only meant to be used during the development phases of an
 application, thus you should not use contracts to try and detect invalid
 input data for a function. Contract code should be used to detect logic
 errors and not data errors.
 Derek
Actually Derek, contracts as a concept have no such limitation. Their current implementation only performs logic errors, but logic errors may be extended to include proof-by-induction number range theories. The contract specification could be used to generate spectral graphing of function i/o, utilize such to raise errors, document exacting constraints, etc.; however nobody has yet programmed such a useful tool. It would probably be as complicated as gaining access to the AST for D (or parsing it) and then performing a hint of linear algebra; with the added problem of needing to provide cross platform gui for the spectral graph if one was desireable, or simply throw additional errors when a bounds was exceeded or unchecked.
Mar 21 2007
parent Derek Parnell <derek nomail.afraid.org> writes:
On Wed, 21 Mar 2007 09:44:14 -0400, Dan wrote:

 It is only meant to be used during the development phases of an
 application, thus you should not use contracts to try and detect invalid
 input data for a function. Contract code should be used to detect logic
 errors and not data errors.
 Derek
Actually Derek, contracts as a concept have no such limitation. Their current implementation only performs logic errors ...
I'm sorry if I may have been a bit vague, but I was actually talking about D's implementation of contracts and not *Contracts* as a general program development concept. -- Derek (skype: derek.j.parnell) Melbourne, Australia "Justice for David Hicks!" 22/03/2007 9:36:55 AM
Mar 21 2007
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
Derek Parnell wrote:
 On Wed, 21 Mar 2007 00:35:46 -0800, David B. Held wrote:
 
 Derek Parnell wrote:
 [...]
 The trouble is that D's typedef does not allow you to extend the semantics
 of the base type, but that's what structs and classes are for, no?

 The Euphoria language has a slightly different type construction mechanism
 which has its good side and bad side, but maybe something like it could be
 used to extend D's typedef mechanism.
 [...]
My understanding is that Contracts in D allow you to implement Ada-like SubRanges. This is actually one of the things I really liked about the DbC support. Is that not the case?
Contracts are optional. That is, if something is compiled with the -release switch, then contract code is not inserted into the executable. You cannot, and must not, rely on contract code for 'production' editions of software. It is only meant to be used during the development phases of an application, thus you should not use contracts to try and detect invalid input data for a function. Contract code should be used to detect logic errors and not data errors.
I disagree. That input arguments must be within the proper range, etc, is a precondition, and the purpose of the 'in' clause is to check preconditions. My interpretation of your statement is that data checking is so important that it may not happen as a part of normal precondition checking. The thing is, *all* preconditions are important. Should we just ignore the 'in' clause altogether then? Or assert()? In my opinion, if the user disables contract checking then he is explicitly stating that he isn't interested in that level of security. My only regret is that the current crop of D compilers don't offer any means of linking a separate library for 'debug' and 'release' builds (a bad choice of words for the associated effects). Sean
Mar 21 2007
next sibling parent reply Dan <murpsoft hotmail.com> writes:
Sean Kelly Wrote:
 My only regret is that the current crop of D compilers don't offer any 
 means of linking a separate library for 'debug' and 'release' builds (a 
 bad choice of words for the associated effects).
I think you actually can link in different modules accordingly, via static ifs, version, debug { import x; } etc. I think you mean phobos vs. tango etc. though right?
Mar 21 2007
parent Sean Kelly <sean f4.ca> writes:
Dan wrote:
 Sean Kelly Wrote:
 My only regret is that the current crop of D compilers don't offer any 
 means of linking a separate library for 'debug' and 'release' builds (a 
 bad choice of words for the associated effects).
I think you actually can link in different modules accordingly, via static ifs, version, debug { import x; } etc. I think you mean phobos vs. tango etc. though right?
One problem with pragma(lib) is that it only works if the pragma is in a module being compiled into the program. It has no effect if it is in a 'header' module. So unless bud/rebuild is being used to construct the application, library developers have no way to automatically link a specific library using static if, version, etc. And for something fairly fundamental like Phobos or Tango, I'm not sure it's appropriate to expect the user to explicitly link a debug, release, or debug/release build (yes, both flags can be specified simultaneously in D). Sean
Mar 21 2007
prev sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Wed, 21 Mar 2007 09:49:20 -0700, Sean Kelly wrote:

 Derek Parnell wrote:
 On Wed, 21 Mar 2007 00:35:46 -0800, David B. Held wrote:
 
 Derek Parnell wrote:
 [...]
 The trouble is that D's typedef does not allow you to extend the semantics
 of the base type, but that's what structs and classes are for, no?

 The Euphoria language has a slightly different type construction mechanism
 which has its good side and bad side, but maybe something like it could be
 used to extend D's typedef mechanism.
 [...]
My understanding is that Contracts in D allow you to implement Ada-like SubRanges. This is actually one of the things I really liked about the DbC support. Is that not the case?
Contracts are optional. That is, if something is compiled with the -release switch, then contract code is not inserted into the executable. You cannot, and must not, rely on contract code for 'production' editions of software. It is only meant to be used during the development phases of an application, thus you should not use contracts to try and detect invalid input data for a function. Contract code should be used to detect logic errors and not data errors.
I disagree.
Again, please note that the OP was asking about contracts *as implemented by D*, and that is what I restricted my comments to.
 That input arguments must be within the proper range, etc, 
 is a precondition, and the purpose of the 'in' clause is to check 
 preconditions.  
However, one can compile it out of the code via a -release so I was saying that one must not rely on such contract code actually executing in a production release edition of the program. Yes, input data, especially stuff that originates from outside of the application really ought to be validated at some stage, but 'in' blocks are not the best places if one is using the current implementation of D.
 My interpretation of your statement is that data 
 checking is so important that it may not happen as a part of normal 
 precondition checking.  The thing is, *all* preconditions are important. 
And yet, DigitalMars has decided that 'in' blocks can be optionally compiled. I agree in principle with the idea that input data ought to be validated, but I would recommend that one does not use 'in' blocks to do that important task.
   Should we just ignore the 'in' clause altogether then?  
No, of course not. But using the current D implementation, one should not rely on it for production editions of your application. Use it only for development editions. Then, armed with this implementation of precondition contracts, one should use them to validate the logic of your algorithms and not the validity of incoming data.
 Or assert()? 
No, of course not. But as asserts are not guaranteed to execute in production editions, one should just use them in development editions. Of course, you are quite at liberty to distribute both a 'debugging' edition along with the 'lean&mean' production edition of your application. For example, I do this with Bud. -- Derek (skype: derek.j.parnell) Melbourne, Australia "Justice for David Hicks!" 22/03/2007 9:40:56 AM
Mar 21 2007
next sibling parent Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Derek Parnell wrote:
 On Wed, 21 Mar 2007 09:49:20 -0700, Sean Kelly wrote:
 
 Derek Parnell wrote:
 On Wed, 21 Mar 2007 00:35:46 -0800, David B. Held wrote:

 Derek Parnell wrote:
 [...]
 The trouble is that D's typedef does not allow you to extend the semantics
 of the base type, but that's what structs and classes are for, no?

 The Euphoria language has a slightly different type construction mechanism
 which has its good side and bad side, but maybe something like it could be
 used to extend D's typedef mechanism.
 [...]
My understanding is that Contracts in D allow you to implement Ada-like SubRanges. This is actually one of the things I really liked about the DbC support. Is that not the case?
Contracts are optional. That is, if something is compiled with the -release switch, then contract code is not inserted into the executable. You cannot, and must not, rely on contract code for 'production' editions of software. It is only meant to be used during the development phases of an application, thus you should not use contracts to try and detect invalid input data for a function. Contract code should be used to detect logic errors and not data errors.
I disagree.
Again, please note that the OP was asking about contracts *as implemented by D*, and that is what I restricted my comments to.
 That input arguments must be within the proper range, etc, 
 is a precondition, and the purpose of the 'in' clause is to check 
 preconditions.  
However, one can compile it out of the code via a -release so I was saying that one must not rely on such contract code actually executing in a production release edition of the program. Yes, input data, especially stuff that originates from outside of the application really ought to be validated at some stage, but 'in' blocks are not the best places if one is using the current implementation of D.
 My interpretation of your statement is that data 
 checking is so important that it may not happen as a part of normal 
 precondition checking.  The thing is, *all* preconditions are important. 
And yet, DigitalMars has decided that 'in' blocks can be optionally compiled. I agree in principle with the idea that input data ought to be validated, but I would recommend that one does not use 'in' blocks to do that important task.
   Should we just ignore the 'in' clause altogether then?  
No, of course not. But using the current D implementation, one should not rely on it for production editions of your application. Use it only for development editions. Then, armed with this implementation of precondition contracts, one should use them to validate the logic of your algorithms and not the validity of incoming data.
 Or assert()? 
No, of course not. But as asserts are not guaranteed to execute in production editions, one should just use them in development editions. Of course, you are quite at liberty to distribute both a 'debugging' edition along with the 'lean&mean' production edition of your application. For example, I do this with Bud.
I often take the same approach, and am quite glad that you do so. Whenever I find unexpected sorts of building errors, for example, I revert to using bud_d.exe for subsequent builds until I figure out what's going on, just on the off chance Bud's debug code might catch something I'm not, or there may be an error in Bud itself -- hasn't happened for me yet, but I'm cautious. :) -- Chris Nicholson-Sauls
Mar 22 2007
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
Derek Parnell wrote:
 
 Yes, input data, especially stuff that originates from outside of the
 application really ought to be validated at some stage, but 'in' blocks are
 not the best places if one is using the current implementation of D.
Okay, this is a valid point. I just happen to disagree with it :-) I feel that this is what 'in' contracts are for, and if the user doesn't want that security then it's their prerogative to turn it off.
 My interpretation of your statement is that data 
 checking is so important that it may not happen as a part of normal 
 precondition checking.  The thing is, *all* preconditions are important.  
And yet, DigitalMars has decided that 'in' blocks can be optionally compiled. I agree in principle with the idea that input data ought to be validated, but I would recommend that one does not use 'in' blocks to do that important task.
Or assert, I suppose, since it is disabled too. This leaves D in the same situation as C/C++, and I refuse to do this simply because the user has the option to disable it.
   Should we just ignore the 'in' clause altogether then?  
No, of course not. But using the current D implementation, one should not rely on it for production editions of your application. Use it only for development editions. Then, armed with this implementation of precondition contracts, one should use them to validate the logic of your algorithms and not the validity of incoming data.
But this suggests that I should either do parameter checking both in the 'in' clause and in the function body using separate implementations (since assert is not an option). This complicates maintenance, hurts readability, and reduces the overall utility of the contract programming features IMO.
 Or assert()? 
No, of course not. But as asserts are not guaranteed to execute in production editions, one should just use them in development editions. Of course, you are quite at liberty to distribute both a 'debugging' edition along with the 'lean&mean' production edition of your application. For example, I do this with Bud.
I'd like to do this with Tango, but it is not an option for the simple install (ie. where the full runtime is bundled in phobos.lib), since that lib is implicitly linked. By the way, can someone tell me what is included when both -debug and -release are set? Sean
Mar 22 2007
parent reply Derek Parnell <derek psych.ward> writes:
On Thu, 22 Mar 2007 09:20:01 -0700, Sean Kelly wrote:

 Derek Parnell wrote:
   Should we just ignore the 'in' clause altogether then?  
No, of course not. But using the current D implementation, one should not rely on it for production editions of your application. Use it only for development editions. Then, armed with this implementation of precondition contracts, one should use them to validate the logic of your algorithms and not the validity of incoming data.
But this suggests that I should either do parameter checking both in the 'in' clause and in the function body using separate implementations (since assert is not an option). This complicates maintenance, hurts readability, and reduces the overall utility of the contract programming features IMO.
Huh? Why would you do it twice? That's crazy talk. Just code the validation once; at the start of the 'body' block. The effect is identical to coding it in the 'in' block only *and* not compiling with -release. No need to code the validation twice at all. int doubleit(int x, int y) body { // validate params. { if (y < 0) y = -y; if ((x < -y) || (x > y)) { throw new Exception(ReportErr( RangeErr, x, -y, y)); } } // Do the work now everything's okay. return x + x; } out(res) { assert(res = x*2, format("doubleit(%d): Weird Math Error", x)); } unittest { assert( doubleit(-10) == -20 ); assert( doubleit( -1) == -2 ); assert( doubleit( 0) == 0 ); assert( doubleit( 1) == 2 ); assert( doubleit( 10) == 20 ); try{ doubleit(-111); } catch(Exception e) { writefln("%s", e.msg);} try{ doubleit(-11); } catch(Exception e) { writefln("%s", e.msg);} try{ doubleit( 11); } catch(Exception e) { writefln("%s", e.msg);} try{ doubleit( 111); } catch(Exception e) { writefln("%s", e.msg);} } With this setup, I don't have to be concerned that -release will ignore my out block as the function inputs are still validated.
 By the way, can someone tell me what is 
 included when both -debug and -release are set?
-debug compiles *in* anything that is inside a debug block. e.g. debug { writefln("Got here"); } It has nothing whatsoever to do with -release or -unittest. It is functionally identical to -version except that it allows 'anonymous' blocks. -release does not compile in asserts, 'in' blocks, or 'out' blocks. -- Derek Parnell Melbourne, Australia "Justice for David Hicks!" skype: derek.j.parnell
Mar 22 2007
parent reply Dan <murpsoft hotmail.com> writes:
Derek Parnell Wrote:
 Huh? Why would you do it twice? That's crazy talk. Just code the validation
 once; at the start of the 'body' block. The effect is identical to coding
 it in the 'in' block only *and* not compiling with -release. No need to
 code the validation twice at all.
 
   int doubleit(int x, int y)
   body
   {
       // validate params.
       {
            if (y < 0) y = -y;
            if ((x < -y) || (x > y))
            {
                throw new Exception(ReportErr( RangeErr, x, -y, y));
            }
       }
 
       // Do the work now everything's okay.
       return x + x;
   }
   out(res)
   {
      assert(res = x*2, format("doubleit(%d): Weird Math Error", x));
   }
IMHO: Yuck. Here's what I think *ought* to be done to solve the problem of guaranteeing that code is error free for input ranges; int doubleit(int x, int y) in { // note: you say if y is negative, make it positive twice and test <-y? // you then tested the original proposition ALSO! assert(x > y); } body { return x+x; } out(result) { assert( result == x*2, format("doubleit(%d): Weird Math Error", x) ); } What should then happen is this: The compiler should recognize that we only accept values such that x > y. Knowing that, it should then verify that in all cases where this function can be called, that the input to the function must have this condition. In cases where this *can* fail, it should raise an error - such as variables that are affected by user input or outside parameters that aren't already bounds checked or adjusted... Theoretically, one could take any given int (between int.min and int.max) and adjust it such that x > y will be true for all cases. This can be proven and verified by a proof by induction. If you don't isolate the in/out tests from the body, a proof by induction couldn't know what needs to be proved to prove the code safe. For such a system, you also actually do want to simply provide warnings for some cases; such as for example: int qsort(int x, int y){ int z = x + y; } In most cases, that would be considered bad, because if x + y exceeds int.max, you have an overflow. That may in fact be the programmers intention however - so refusing to compile would be the wrong way to approach the problem. Walter, I'm not sure, but I think you were hoping (or did) make the Abstract Symbol Tree accessible? If so, it may be possible to start a dsource project to accomplish a proof by induction engine based on the current implementation of contract programming. Sincerely, Dan
Mar 22 2007
next sibling parent Derek Parnell <derek nomail.afraid.org> writes:
On Thu, 22 Mar 2007 14:54:20 -0400, Dan wrote:

 Derek Parnell Wrote:
 
 IMHO: Yuck.
I just knew that if I didn't write an off-the-cuff example that wasn't 110% perfect I'd have some people pooh-poohing the concept I was trying to get across. Sheesh - it was 3:58AM when I wrote that after having been up since 6:30AM the previous day!
 Here's what I think *ought* to be done to solve the problem of
 guaranteeing that code is error free for input ranges;
That was not the point of the example! The point was, using current D, the 'in' block can never be guaranteed to be executed so don't rely on using the 'in' block for parameter validation - whatever that validation happens to be.
 int doubleit(int x, int y)
 in {
   // note: you say if y is negative, make it positive twice and test <-y?
   // you then tested the original proposition ALSO!
Get a grip, eh? It was a silly example and didn't mean to represent best practice. The actual code used is totally irrelevant to my argument.
   assert(x > y);
 }
Now, if this is compiled using '-release' then your precious assert test is quietly ignored and the user gets a nasty run time error, as opposed to a nice run time error.
 body {
   return x+x;
 }
 out(result) {
   assert( result == x*2, format("doubleit(%d): Weird Math Error", x) );
 }
 
 What should then happen is this:  The compiler should recognize that
 we only accept values such that x > y.  Knowing that, it should then
 verify that in all cases where this function can be called, that the
 input to the function must have this condition.  In cases where this
 *can* fail, it should raise an error - such as variables that are
 affected by user input or outside parameters that aren't already
 bounds checked or adjusted...
If I was to ask for an improvement in the compiler, with respect to assert() and in{}, it would be to enable us to target exactly which parts of the DbC to excluded in a build. Personally, if I could, I'd never exclude in{} or out{} blocks with the -release switch. Instead I'd like to code them more like ... int somefunc( ... ) in { ...validate inputs ...} body { ... } debug(logic) out {} and then when developing I'd compile with -debug=logic and when compiling the production edition I'd just leave out the -debug switch but use -release to skip over assert(). -- Derek (skype: derek.j.parnell) Melbourne, Australia "Justice for David Hicks!" 23/03/2007 1:02:58 PM
Mar 22 2007
prev sibling next sibling parent reply Kevin Bealer <kevinbealer gmail.com> writes:
Dan wrote:
...
 What should then happen is this:  The compiler should recognize that we
 only accept values such that x > y.  Knowing that, it should then verify
 that in all cases where this function can be called, that the input to the
 function must have this condition.  In cases where this *can* fail, it
 should raise an error - such as variables that are affected by user input
 or outside parameters that aren't already bounds checked or adjusted...
 
 Theoretically, one could take any given int (between int.min and int.max)
 and adjust it such that x > y will be true for all cases.  This can be
 proven and verified by a proof by induction.
 
 If you don't isolate the in/out tests from the body, a proof by induction
 couldn't know what needs to be proved to prove the code safe.
I think there are very few cases where this kind of induction could reasonably be done, unless x and y are literals. I suspect that in most cases where the compiler had enough info to do this kind of induction checking, the code for the function could nearly as easily be "constant folded" and optimized away entirely. Prove me wrong, but I suspect that this kind of induction is too hard (and in the cases where it works, too slow) to be practical. If x and y are literals, though, you can do something like this now: Original source: int foo(int x, int y, int z) { assert( x > y ); // runtime ... } int z = foo(10, 20, 30); Checking at compile time: int foo(int x, int y)(int z) { static assert( x > y ); // compile time ... } int z = foo!(10, 20)(30); It's a very simple transformation --- take any compile-time known values and make them template parameters. In this case, the calling syntax even looks nearly the same! For most of the practical cases where something is induction testable at compile time, this technique should get what you are suggesting. You are marking the path of x and y, and the compiler will do the actual induction via constant folding. If you don't have a constant 10 and 20 for x and y, then propagate the "templatization" of x and y up the call stack (making them template parameters at every level) until you get to the point where x and y are created or computed. *Note: if you find a place where you can't compute x and y at compile time, induction would also have failed there. Actually ... it would be interesting to fiddle with a compiler that does this "templatization" for all compile time known parameters, as an optimization --- it would have to be a global optimization of course. I heard many years ago about an idea that you could take some of the input for a program and use it to optimize the entire program. For example, you could take a python program and a python interpreter, and insert the python program into the "main" of the interpreter. The idea was that you could then "optimize" the interpreter + script until both together were an optimized 'C' program that was equivalent to the script. In theory, this works -- in real life, probably not very well. Kevin
Mar 22 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Kevin Bealer wrote:
 Dan wrote:
 ...
 What should then happen is this:  The compiler should recognize that we
> only accept values such that x > y. Knowing that, it should then verify
 that in all cases where this function can be called, that the input to 
 the
> function must have this condition. In cases where this *can* fail, it
 should raise an error - such as variables that are affected by user input
> or outside parameters that aren't already bounds checked or adjusted...
 Theoretically, one could take any given int (between int.min and int.max)
> and adjust it such that x > y will be true for all cases. This can be > proven and verified by a proof by induction.
 If you don't isolate the in/out tests from the body, a proof by induction
 couldn't know what needs to be proved to prove the code safe.
I think there are very few cases where this kind of induction could reasonably be done, unless x and y are literals. I suspect that in most cases where the compiler had enough info to do this kind of induction checking, the code for the function could nearly as easily be "constant folded" and optimized away entirely. Prove me wrong, but I suspect that this kind of induction is too hard (and in the cases where it works, too slow) to be practical. If x and y are literals, though, you can do something like this now: Original source: int foo(int x, int y, int z) { assert( x > y ); // runtime ... } int z = foo(10, 20, 30); Checking at compile time: int foo(int x, int y)(int z) { static assert( x > y ); // compile time ... } int z = foo!(10, 20)(30); It's a very simple transformation --- take any compile-time known values and make them template parameters. In this case, the calling syntax even looks nearly the same!
The need to cater for cases with partial statically-available information is seriously taken into consideration. The general scenario is called "partial evaluation": you call a function with a few statically-known values and a few runtime values. The current compile-time evaluation mechanism can't help with that. So a considered addition to D is to accommodate static parameters (pushed by yours truly and using a syntax suggested by Dave Held): int foo(int x, int y, int z); int foo(static int x, int y, int z); // x is known during compilation int foo(int x, static int y, int z); // y is known during compilation int foo(static int x, static int y, int z); // x and y are... you know Seeing that, the compiler does a little trick - it transforms each "static" parameter into a template. For example, the second overload becomes under the covers: int foo(int x)(int y, int z) { ... you can use e.g. static if (x > 0) ... } Then the foo's will overload without you doing anything special: final a = to!(int)(readln), b = to!(int)(readln); foo(a, b, b); // firsts overload foo(1, a + b, b); // 2nd foo(a + b, a - b, b); // 3rd foo(42, 7, b); // 4th Such partial evaluation scenarios are very useful for a host of functions (writef/readf are the first to come to mind). Consider a simple example: the function: uint match_one_of(char[] set, char[] candidate); Returns the position in candidate up to which candidate's characters can be found inside set. For example, find("0123456789", "23a4") returns 2. We have quite a few different evaluation scenarios: 1. set and candidate aren't known til runtime. Then find will gotta do whatever it has to do dynamically to get the task done. 2. set is known statically, and candidate is dynamic. Well, this is a very interesting case. The function can decide for different strategies depending on set's size. For small sizes, it just does a series of comparisons. For moderate sizes, linear search. For large sizes, hash. Cool! 3. candidate is known statically, set is dynamic. Probably this is an infrequent case. Anyhow, all duplicate characters in candidate can be removed upon searching, thus again saving time. 4. Both are known statically. The case becomes again uninteresting as D's constant folding can just invoke (1) during compilation. Done. Andrei
Mar 22 2007
next sibling parent reply "David B. Held" <dheld codelogicconsulting.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 [...]
 int foo(int x, int y, int z);
 int foo(static int x, int y, int z); // x is known during compilation
 int foo(int x, static int y, int z); // y is known during compilation
 int foo(static int x, static int y, int z); // x and y are... you know
 [...]
 final a = to!(int)(readln), b = to!(int)(readln);
 foo(a, b, b);  // firsts overload
 foo(1, a + b, b); // 2nd
 foo(a + b, a - b, b);  // 3rd
 foo(42, 7, b); // 4th
 [...]
Great sir, could you impart upon us mere mortals the wisdom by which you are able to ascertain, at compile time, the difference between two values known only at runtime (a la the "3rd" overload)? We are in great need of such Oracle-like behavior. Dave
Mar 22 2007
next sibling parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
David B. Held wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 [...]
 int foo(int x, int y, int z);
 int foo(static int x, int y, int z); // x is known during compilation
 int foo(int x, static int y, int z); // y is known during compilation
 int foo(static int x, static int y, int z); // x and y are... you know
 [...]
 final a = to!(int)(readln), b = to!(int)(readln);
 foo(a, b, b);  // firsts overload
 foo(1, a + b, b); // 2nd
 foo(a + b, a - b, b);  // 3rd
 foo(42, 7, b); // 4th
 [...]
Great sir, could you impart upon us mere mortals the wisdom by which you are able to ascertain, at compile time, the difference between two values known only at runtime (a la the "3rd" overload)? We are in great need of such Oracle-like behavior.
Errare humanum, perseverare diabolicum est :o). Andrei
Mar 22 2007
prev sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
David B. Held wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 [...]
 int foo(int x, int y, int z);
 int foo(static int x, int y, int z); // x is known during compilation
 int foo(int x, static int y, int z); // y is known during compilation
 int foo(static int x, static int y, int z); // x and y are... you know
 [...]
 final a = to!(int)(readln), b = to!(int)(readln);
 foo(a, b, b);  // firsts overload
 foo(1, a + b, b); // 2nd
 foo(a + b, a - b, b);  // 3rd
 foo(42, 7, b); // 4th
 [...]
Great sir, could you impart upon us mere mortals the wisdom by which you are able to ascertain, at compile time, the difference between two values known only at runtime (a la the "3rd" overload)? We are in great need of such Oracle-like behavior.
I also notice you're still posting from the future. You must make good money daytrading. Future postulae commotus est. :o) Andrei
Mar 22 2007
parent reply "David B. Held" <dheld codelogicconsulting.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 David B. Held wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 [...]
 int foo(int x, int y, int z);
 int foo(static int x, int y, int z); // x is known during compilation
 int foo(int x, static int y, int z); // y is known during compilation
 int foo(static int x, static int y, int z); // x and y are... you know
 [...]
 final a = to!(int)(readln), b = to!(int)(readln);
 foo(a, b, b);  // firsts overload
 foo(1, a + b, b); // 2nd
 foo(a + b, a - b, b);  // 3rd
 foo(42, 7, b); // 4th
 [...]
Great sir, could you impart upon us mere mortals the wisdom by which you are able to ascertain, at compile time, the difference between two values known only at runtime (a la the "3rd" overload)? We are in great need of such Oracle-like behavior.
I also notice you're still posting from the future. You must make good money daytrading. Future postulae commotus est. :o)
Hmm...and I'm equally sure you're posting from the past. My watch currently says 11:45 PM, my last post shows 10:48 PM (which I think is accurate), and your follow-up shows 10:02 PM (which was just a few impossibly short moments ago, by my reckoning). The only way I can explain this discrepancy is that we have an extreme relative velocity (perhaps on the order of 0.99999998c?). The other possibility is that your OS is not patched for DST. Dave
Mar 23 2007
next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
David B. Held wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 David B. Held wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 [...]
 int foo(int x, int y, int z);
 int foo(static int x, int y, int z); // x is known during compilation
 int foo(int x, static int y, int z); // y is known during compilation
 int foo(static int x, static int y, int z); // x and y are... you know
 [...]
 final a = to!(int)(readln), b = to!(int)(readln);
 foo(a, b, b);  // firsts overload
 foo(1, a + b, b); // 2nd
 foo(a + b, a - b, b);  // 3rd
 foo(42, 7, b); // 4th
 [...]
Great sir, could you impart upon us mere mortals the wisdom by which you are able to ascertain, at compile time, the difference between two values known only at runtime (a la the "3rd" overload)? We are in great need of such Oracle-like behavior.
I also notice you're still posting from the future. You must make good money daytrading. Future postulae commotus est. :o)
Hmm...and I'm equally sure you're posting from the past. My watch currently says 11:45 PM, my last post shows 10:48 PM (which I think is accurate), and your follow-up shows 10:02 PM (which was just a few impossibly short moments ago, by my reckoning). The only way I can explain this discrepancy is that we have an extreme relative velocity (perhaps on the order of 0.99999998c?). The other possibility is that your OS is not patched for DST. Dave
Andrei posts at 4:32 PM, you post at 5:48 PM, Andrei replies at 5:02 PM, and then you reply to him at 6:46 PM. It's currently 6:04 PM :) -- Daniel -- int getRandomNumber() { return 4; // chosen by fair dice roll. // guaranteed to be random. } http://xkcd.com/ v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
Mar 23 2007
parent "David B. Held" <dheld codelogicconsulting.com> writes:
Daniel Keep wrote:
 
 [...]
 Andrei posts at 4:32 PM, you post at 5:48 PM, Andrei replies at 5:02 PM,
 and then you reply to him at 6:46 PM.
 
 It's currently 6:04 PM :)
Then I'm sure it's because I'm still running W2K *gasp*, which is an officially unsupported OS. I still get "security" patches, but nothing as luxurious as a DST patch. I could patch the registry myself, but why bother when the problem will fix itself in a week? ;> Dave
Mar 23 2007
prev sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
David B. Held wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 David B. Held wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 [...]
 int foo(int x, int y, int z);
 int foo(static int x, int y, int z); // x is known during compilation
 int foo(int x, static int y, int z); // y is known during compilation
 int foo(static int x, static int y, int z); // x and y are... you know
 [...]
 final a = to!(int)(readln), b = to!(int)(readln);
 foo(a, b, b);  // firsts overload
 foo(1, a + b, b); // 2nd
 foo(a + b, a - b, b);  // 3rd
 foo(42, 7, b); // 4th
 [...]
Great sir, could you impart upon us mere mortals the wisdom by which you are able to ascertain, at compile time, the difference between two values known only at runtime (a la the "3rd" overload)? We are in great need of such Oracle-like behavior.
I also notice you're still posting from the future. You must make good money daytrading. Future postulae commotus est. :o)
Hmm...and I'm equally sure you're posting from the past. My watch currently says 11:45 PM, my last post shows 10:48 PM (which I think is accurate), and your follow-up shows 10:02 PM (which was just a few impossibly short moments ago, by my reckoning). The only way I can explain this discrepancy is that we have an extreme relative velocity (perhaps on the order of 0.99999998c?). The other possibility is that your OS is not patched for DST.
It's a timezone thing. What's your OS timezone? Your posts show -0800 time shift, while other people's in the same timezone show -0700. I know you have nostalgia for your old place, but it's about time you move to Seattle entirely :o). Andrei
Mar 23 2007
parent reply Dan <murpsoft hotmail.com> writes:
 Andrei Alexandrescu (See Website For Email) wrote:
 [...]
 int foo(int x, int y, int z);
 int foo(static int x, int y, int z); // x is known during compilation
 int foo(int x, static int y, int z); // y is known during compilation
 int foo(static int x, static int y, int z); // x and y are... you know
 [...]
 final a = to!(int)(readln), b = to!(int)(readln);
 foo(a, b, b);  // firsts overload
 foo(1, a + b, b); // 2nd
 foo(a + b, a - b, b);  // 3rd
 foo(42, 7, b); // 4th
 [...]
Great sir, could you impart upon us mere mortals the wisdom by which you are able to ascertain, at compile time, the difference between two values known only at runtime (a la the "3rd" overload)? We are in great need of such Oracle-like behavior.
I also notice you're still posting from the future. You must make good money daytrading. Future postulae commotus est. :o)
Dear sirs, I have such wisdom. Your example is absolutely packed with bugs. First, you are reading an entire line into an int. May I pass the pi? I can tell you that the range of a uint is 0..4294967295. I also say with cofidence that int is -2147483648..2147483647. Knowing that, good sirs, you are most likely unwittingly causing a bug by calling foo(a+b) where a and b are ints. I say most likely because you may intend for the program to overflow if(a+b > int.max) I can also say that if you add any two unbound integers and attempt to store them in another integer, you are likewise creating an opportunity for a bug. So... let's bound them shall we? void moveTo(Item it, int x, int y) in { assert(x > 0); assert(y > 0 && y < SCREEN.MAXHEIGHT); } body { it.left = x; it.top = y; } ... elsewhere... void setLayout(int a, ubyte b) { moveTo(a-350,b); } We now know that in this case, a had better be >= 350, or our assert ought to fail. In fact, it's a bug, because it *could* be < 350. We also know the upper bound is no longer 2147483647, but 2147483297 for x. For y, we know that the possible bounds are in fact only 0-255, while the function will accept anything between 0 and SCREEN.MAXHEIGHT, whatever arbitrary number that ought to be. So we do know something about it, and we can proveably demonstrate that the program will work for all cases, or it won't with variables being used. Welcome to discrete mathematics 101.
Mar 23 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Dan wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 [...]
 int foo(int x, int y, int z);
 int foo(static int x, int y, int z); // x is known during compilation
 int foo(int x, static int y, int z); // y is known during compilation
 int foo(static int x, static int y, int z); // x and y are... you know
 [...]
 final a = to!(int)(readln), b = to!(int)(readln);
 foo(a, b, b);  // firsts overload
 foo(1, a + b, b); // 2nd
 foo(a + b, a - b, b);  // 3rd
 foo(42, 7, b); // 4th
 [...]
Great sir, could you impart upon us mere mortals the wisdom by which you are able to ascertain, at compile time, the difference between two values known only at runtime (a la the "3rd" overload)? We are in great need of such Oracle-like behavior.
I also notice you're still posting from the future. You must make good money daytrading. Future postulae commotus est. :o)
Dear sirs, I have such wisdom. Your example is absolutely packed with bugs. First, you are reading an entire line into an int. May I pass the pi?
The point of using readln was to clarify that the symbols were dynamically bound. Andrei
Mar 23 2007
prev sibling next sibling parent reply Reiner Pope <reiner none.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Kevin Bealer wrote:
 Dan wrote:
 ...
 What should then happen is this:  The compiler should recognize that we
> only accept values such that x > y. Knowing that, it should then verify
 that in all cases where this function can be called, that the input 
 to the
> function must have this condition. In cases where this *can* fail, it
 should raise an error - such as variables that are affected by user 
 input
> or outside parameters that aren't already bounds checked or adjusted...
 Theoretically, one could take any given int (between int.min and 
 int.max)
> and adjust it such that x > y will be true for all cases. This can be > proven and verified by a proof by induction.
 If you don't isolate the in/out tests from the body, a proof by 
 induction
 couldn't know what needs to be proved to prove the code safe.
I think there are very few cases where this kind of induction could reasonably be done, unless x and y are literals. I suspect that in most cases where the compiler had enough info to do this kind of induction checking, the code for the function could nearly as easily be "constant folded" and optimized away entirely. Prove me wrong, but I suspect that this kind of induction is too hard (and in the cases where it works, too slow) to be practical. If x and y are literals, though, you can do something like this now: Original source: int foo(int x, int y, int z) { assert( x > y ); // runtime ... } int z = foo(10, 20, 30); Checking at compile time: int foo(int x, int y)(int z) { static assert( x > y ); // compile time ... } int z = foo!(10, 20)(30); It's a very simple transformation --- take any compile-time known values and make them template parameters. In this case, the calling syntax even looks nearly the same!
The need to cater for cases with partial statically-available information is seriously taken into consideration. The general scenario is called "partial evaluation": you call a function with a few statically-known values and a few runtime values. The current compile-time evaluation mechanism can't help with that. So a considered addition to D is to accommodate static parameters (pushed by yours truly and using a syntax suggested by Dave Held): int foo(int x, int y, int z); int foo(static int x, int y, int z); // x is known during compilation int foo(int x, static int y, int z); // y is known during compilation int foo(static int x, static int y, int z); // x and y are... you know Seeing that, the compiler does a little trick - it transforms each "static" parameter into a template. For example, the second overload becomes under the covers:
Is this in contrast to the int foo(const int x)(const int x, int y, int z); syntax you mentioned earlier? If so, then I am very grateful you now support the much cleaner syntax you now do. However, while I don't dispute that this is useful, aren't there also times when you want the compiler to do that implicitly? (Maybe the compiler already does, or there are plans to do so in the future, in which case you can ignore the below. If the compiler already does, though, I think this should be documented) Often, the compiler can optimize the function just through const-folding, without needing the coder to write all the overloads. However, const-folding across function calls doesn't seem to be done in DMD unless the function is inlined. Consider the example everyone gives with partial eval: double pow(long exponent, double base) { if (exponent < 0) return pow(-exponent, base); if (exponent%2 == 0) { auto val = pow(exponent/2, base); return val * val; } else { return base * pow(exponent-1, base); } } Suppose this is too big to be inlined by DMD. Do you then have to write an identical overload for a statically-known exponent? double pow(static long exponent, double base) { ... // A duplicate of the above code } I suppose you could allow templating by staticness, but even that is still wrong. If the compiler sees a function call with *some* statically-known parameters, shouldn't it automatically create a template and see how much it can const-fold. If nothing, scrap the template and go back to the old version. Having to actually write the overload seems like an optimization as stupid as having to write 'inline' or 'register'. Cheers, Reiner
Mar 22 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Reiner Pope wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
[snip]
 Is this in contrast to the
 
     int foo(const int x)(const int x, int y, int z);
 
 syntax you mentioned earlier? If so, then I am very grateful you now 
 support the much cleaner syntax you now do.
Yes. The cleaned-up syntax was Dave's idea.
 However, while I don't dispute that this is useful, aren't there also 
 times when you want the compiler to do that implicitly? (Maybe the 
 compiler already does, or there are plans to do so in the future, in 
 which case you can ignore the below. If the compiler already does, 
 though, I think this should be documented)
I don't know. Right now I don't think so. The whole point of overloading is to write "intelligent" custom code that implements the given semantics in various ways. I don't think the compiler can generally do that with today's technology.
 Often, the compiler can optimize the function just through 
 const-folding, without needing the coder to write all the overloads. 
 However, const-folding across function calls doesn't seem to be done in 
 DMD unless the function is inlined.
And I think that's good. You don't want to send the compiler for a wild goose chase so that it runs 100 times slower.
 Consider the example everyone gives with partial eval:
 
 double pow(long exponent, double base)
 {
     if (exponent < 0) return pow(-exponent, base);
     if (exponent%2 == 0)
     {
         auto val = pow(exponent/2, base);
         return val * val;
     }
     else
     {
         return base * pow(exponent-1, base);
     }
 }
 
 Suppose this is too big to be inlined by DMD. Do you then have to write 
 an identical overload for a statically-known exponent?
 
 double pow(static long exponent, double base)
 {
     ... // A duplicate of the above code
 }
I'm afraid so. The compiler can't read your mind and figure that it would be good to fold. If such code is frequent enough, probably we'll provide a way to avoid source duplication. (Btw you got the parameter order backwards.)
 I suppose you could allow templating by staticness, but even that is 
 still wrong. If the compiler sees a function call with *some* 
 statically-known parameters, shouldn't it automatically create a 
 template and see how much it can const-fold. If nothing, scrap the 
 template and go back to the old version.
 
 Having to actually write the overload seems like an optimization as 
 stupid as having to write 'inline' or 'register'.
That's for the future. One step at a time. The technology is not there yet, and compile times still matter. Don't forget that in 1970 "register" and in 1980 "inline" were ideas of much practical value. Andrei
Mar 22 2007
prev sibling parent Don Clugston <dac nospam.com.au> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Kevin Bealer wrote:
 Dan wrote:
 ...
 What should then happen is this:  The compiler should recognize that we
> only accept values such that x > y. Knowing that, it should then verify
 that in all cases where this function can be called, that the input 
 to the
> function must have this condition. In cases where this *can* fail, it
 should raise an error - such as variables that are affected by user 
 input
> or outside parameters that aren't already bounds checked or adjusted...
 Theoretically, one could take any given int (between int.min and 
 int.max)
> and adjust it such that x > y will be true for all cases. This can be > proven and verified by a proof by induction.
 If you don't isolate the in/out tests from the body, a proof by 
 induction
 couldn't know what needs to be proved to prove the code safe.
I think there are very few cases where this kind of induction could reasonably be done, unless x and y are literals. I suspect that in most cases where the compiler had enough info to do this kind of induction checking, the code for the function could nearly as easily be "constant folded" and optimized away entirely. Prove me wrong, but I suspect that this kind of induction is too hard (and in the cases where it works, too slow) to be practical. If x and y are literals, though, you can do something like this now: Original source: int foo(int x, int y, int z) { assert( x > y ); // runtime ... } int z = foo(10, 20, 30); Checking at compile time: int foo(int x, int y)(int z) { static assert( x > y ); // compile time ... } int z = foo!(10, 20)(30); It's a very simple transformation --- take any compile-time known values and make them template parameters. In this case, the calling syntax even looks nearly the same!
The need to cater for cases with partial statically-available information is seriously taken into consideration. The general scenario is called "partial evaluation": you call a function with a few statically-known values and a few runtime values. The current compile-time evaluation mechanism can't help with that. So a considered addition to D is to accommodate static parameters (pushed by yours truly and using a syntax suggested by Dave Held): int foo(int x, int y, int z); int foo(static int x, int y, int z); // x is known during compilation int foo(int x, static int y, int z); // y is known during compilation int foo(static int x, static int y, int z); // x and y are... you know
I pushed for essentially the same thing, about six months ago: http://lists.puremagic.com/pipermail/digitalmars-d/2006-October/008987.html and the syntax I proposed was: int foo(template int x, template int y, int z) { static if (is(x: const) && is(y: const)) { ... } else return simple_foo(x); } I think the concept (in whichever form) is a killer feature; the potential for optimisation is incredible.
Mar 23 2007
prev sibling parent Reiner Pope <reiner none.com> writes:
Dan wrote:
 What should then happen is this:  The compiler should recognize that we only
accept values such that x > y.  Knowing that, it should then verify that in all
cases where this function can be called, that the input to the function must
have this condition.  
Perhaps I'm misunderstanding what you say, but it seems like you are saying that the compiler could *always* analyse programs to determine whether the assert will ever be triggered. This is impossible for the general case as it solves the halting problem (you can reduce all exit conditions to x > y things).
 int doubleit(int x, int y)
 in {
   // note: you say if y is negative, make it positive twice and test <-y?
   // you then tested the original proposition ALSO!
   assert(x > y);
 }
 body {
   return x+x;
 }
 out(result) {
   assert( result == x*2, format("doubleit(%d): Weird Math Error", x) );
 }
...
 If you don't isolate the in/out tests from the body, a proof by induction
couldn't know what needs to be proved to prove the code safe.
I agree with how you would code it here, although I wouldn't expect the compiler to verify all my conditions at compile-time. To me, the point about the 'in' contracts is that they will only ever trigger at a bug. You hope not to have any of these when you release, so you are spending unnecessary processing time checking things which should always be true anyway. If there is a bug, then either way you get a crash for the end user; an assert failure isn't really so much more pleasant than a segmentation violation for an end user.
 Walter, I'm not sure, but I think you were hoping (or did) make the Abstract
Symbol Tree accessible?  If so, it may be possible to start a dsource project
to accomplish a proof by induction engine based on the current implementation
of contract programming.
Such a program would surely be very useful, but I'm not convinced it should be done through compile-time programming (automated theorem proving takes a long time: you wouldn't want to run it *every time* you compile, and you also wouldn't want to run it through CTFE, which is slow). If anyone is interested, though, Sage (http://sage.soe.ucsc.edu/) and which attempt to statically verify as much in the contracts as possible. Cheers, Reiner
Mar 22 2007
prev sibling next sibling parent janderson <askme me.com> writes:
Henning Hasemann wrote:
 I just start a few little things.
 
 1.)
 Most of the bugs I had when starting with D where that I simply forgot
 to initialise members in the c'tor:
 
 class Foo {
   void foo() { ... };
 }
 
 class Bar {
   Foo myFoo;
   void bar() {
     myFoo.foo();
   }
 }
 
 Of course, because here myFoo is default initialised to null this always gives
 a segfault where myFoo is first being used in such a way,
 so it is very easy to track down (if you use a debugger at least).
 
 But sometimes I'd find it nice if there was a way to tell D:
 Warn me at compile time if it is clear that some class instance members
 will be null.
 
 Of course you must be able to tell D exceptions to this, as you will want
 to have a few members be null until later.
 
 I have no good idea what this tool would syntactically look like or
 if it would make sense to have it a compiler switch or whatever.
 Probably the idea is almost bullsh*t nevertheless, because you get
 used to initialise your members after a short while.
initialized. Some C++ versions have warnings (which I always set as errors) that tells you if a variable in a function is never initialized. That's a start at least. Perhaps D could do the same. In effect the private keyword becomes the checker for you. [snip]
 3.)
 Please change the implicit integral casting rules to be more c-like.
 This has been discussed here and I cant remember a good argument
 against casting only in such a way data doesnt get lost.
 (ie: int->float is okay, but float->int, or int->uint is not)
 I had errors that where very hard to track down, because somthing like
 (-1 * 4u) yields the unsigned int representation of -4 which is
 4294967292, which is rather unintuitive when there is no explicit cast.
int->float, I don't agree this is ok however int->double is in my books. Anything that has potential for data loss, you should have to explicitly cast. I generally agree though.
 
 I know, changing this might break some code, but I cant help considering
 code that relies on such implicit casting rules broken nevertheless.
Agreed.
 Henning
 
Mar 19 2007
prev sibling parent BCS <BCS pathlink.com> writes:
Henning Hasemann wrote:
 I just start a few little things.
 
 1.)
[...]
 Of course, because here myFoo is default initialised to null this always gives
 a segfault where myFoo is first being used in such a way,
 so it is very easy to track down (if you use a debugger at least).
[...]
 2.)
 Another thing would be easy support for stack traced exceptions.
 I tried flectioned last weak or so (just for this purpose) but it
 didnt want to compile with my code (I think I was still on dmd-1.007
 so it might have been a bug there). I think STE's are of use
 to many developers so they should be put into phobos or so.
I have found a handy way to catch seg-v's is by catching SIGSEGV with a function that throws an exception, that way I can get a stack trace using my DIY stack tracing. The first line in each function I write is: scope(failure) writef("Backtrace "__FILE__":"~itoa!(__LINE__)~\n); it works just fine as long as you don't ever throw exceptions in normal code paths
Mar 19 2007
prev sibling next sibling parent reply Jascha Wetzel <"[firstname]" mainia.de> writes:
 "Use a debugger only as a last resort. Having to resort to a debugger
 means your programming methodologies have failed."
i strongly disagree with this statement. you can do a lot more with a debugger than printing exception stacktraces and finding uninitialized variables. it enables you to conveniently watch your program work step by step, watch intermediate values, etc. to check whether it does the right thing, not just on a low, technical level. no syntax or programming methodologies will prevent you from implementing faulty algorithms, they'll just help you to not fall into pits that are part of the details of a programming language. Knud Soerensen wrote:
 This morning I stumbled over a on-line book named "Writing Bug-Free C Code"
 http://www.duckware.com/bugfreec/index.html 
 the gem of the book (chapter 4) is a way to implement an object model which
 provide better data hiding than C++ classes.
 
 It also gives advise like:
 "Use a debugger only as a last resort. Having to resort to a debugger means
 your programming methodologies have failed."
 and 
 "When fixing a bug, fix the underlying problem or cause of the bug and not
just the bug itself." 
 
 D already have many feature which help the programmer make lesser
 bugs, but how can we make it even better ?
 
 I think a good place to start is look at the last bugs reported by the
 users in released D programs, and consider if it is coursed by some
 fundamental flaw in D and what can be done to catch it earlier in 
 development.  
 
 Maybe we need a place to report and discus this type of bugs.
 Is the newsgroup okay or would a bug-tracking system be a better option ?
Mar 21 2007
parent reply Knud Soerensen <4tuu4k002 sneakemail.com> writes:
On Wed, 21 Mar 2007 13:16:08 +0100, Jascha Wetzel wrote:

 "Use a debugger only as a last resort. Having to resort to a debugger
 means your programming methodologies have failed."
i strongly disagree with this statement. you can do a lot more with a debugger than printing exception stacktraces and finding uninitialized variables. it enables you to conveniently watch your program work step by step, watch intermediate values, etc. to check whether it does the right thing, not just on a low, technical level. no syntax or programming methodologies will prevent you from implementing faulty algorithms, they'll just help you to not fall into pits that are part of the details of a programming language.
Yes, the debugger can be very useful. What he meant where that if your use a debugger for debugging you obviously got a bug in your program. Then when your find the bug your should go check your methods to see why the bug slipped through. I am working on a set of rules for bug free programing and one of them is "Question every line of code" and here the debugger is very useful, it allow you to single step through you code and question everything. But you should always do this not only when you get a bug. Right now the rules looks like. 1. Bug free programing is possible 2. Every bug is due to a problem in the programming process 3. Question every line of code 4. Everything must scale 5. Test all code 6. Hide your data 7. Count your mistakes 8. Build it right the first time 9. Interfaces are good, but inheritance is bad 10. Always leave objects in a valid state
Mar 21 2007
parent Sean Kelly <sean f4.ca> writes:
Knud Soerensen wrote:
 
 I am working on a set of rules for bug free programing
 and one of them is "Question every line of code" 
 and here the debugger is very useful, it allow you to 
 single step through you code and question everything.
 But you should always do this not only when you get a bug.
  
 Right now the rules looks like.
 
 
    1. Bug free programing is possible
    2. Every bug is due to a problem in the programming process
    3. Question every line of code
    4. Everything must scale
    5. Test all code
    6. Hide your data
    7. Count your mistakes
    8. Build it right the first time
    9. Interfaces are good, but inheritance is bad
   10. Always leave objects in a valid state
This is a good semi-related article: http://www.fastcompany.com/online/06/writestuff.html Sean
Mar 21 2007
prev sibling parent Dan <murpsoft hotmail.com> writes:
Reiner Pope Wrote:
 If you don't isolate the in/out tests from the body, a proof by induction
couldn't know what needs to be proved to prove the code safe.
I agree with how you would code it here, although I wouldn't expect the compiler to verify all my conditions at compile-time.
I agree m8. It should be a separate feature and/or program that you run when your program compiles and you want to make it proveably correct (bug-free)
 To me, the point 
 about the 'in' contracts is that they will only ever trigger at a bug. 
To me, bug = failure. As programmers, we often accept six dozen failures in a program without realizing it and then find out 7 years down the road (see the libc quicksort bug). If we could have a tool to help us completely eliminate bugs, wouldn't that be awesome? Contract programming *allows* us to, it doesn't by default though.
 You hope not to have any of these when you release, so you are spending 
 unnecessary processing time checking things which should always be true 
 anyway.
 
 If there is a bug, then either way you get a crash for the end user; an 
 assert failure isn't really so much more pleasant than a segmentation 
 violation for an end user.
Not always, sometimes it just gives you no answer, or worse, the wrong one. Sometimes it works for all cases but exactly one number, which means you find it years and years down the road and a 38million dollar system crashes because of it. Picture if that was how the libc quicksort bug was found... libc is thought to be stable.
 Such a program would surely be very useful, but I'm not convinced it 
 should be done through compile-time programming (automated theorem 
 proving takes a long time: you wouldn't want to run it *every time* you 
 compile, and you also wouldn't want to run it through CTFE, which is slow).
It only has to parse and maintain a set of possible min/max pairs. I'm suggesting it would be possible to prove by induction for non-constants and constants alike; by examining how they are modified through possible program flows, and verifying that by the time they hit an "in" or "out" they are within acceptable parameters. This would be done by reverse-tracing the potential flow paths up to the declaration of the variable, and then doubling back fetching possible ranges going back down each of those potential flow paths to the result. This has a marked similarity to chess algorithms. Dumping the potential program paths, performing spectral analysis and such would be well beyond the scope of what most programmers are used to. However, I feel with sufficient development it could become almost completely automated. It would also formalize and automated what most senior programmers already try to do in their head.
 
 If anyone is interested, though, Sage (http://sage.soe.ucsc.edu/) and 

 which attempt to statically verify as much in the contracts as possible.
Ooo... sweet. Something *interesting* to read.
 
 Cheers,
 
 Reiner
On that note, Walter, any chance we can get access to the AST?
Mar 23 2007