www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Idea: function contracts + CTFE

reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
I've found myself not using contracts nearly as much as I'd kind of like to. 
Mostly because, well, other than a nice syntactic separation between the 
contracts and the body of the function, there's almost nothing I can do with 
contracts that can't be done with a debug{} block in the function.

The problem boils down to this: no matter how well-defined I make my 
contracts, they will never actually be tested until runtime.  At that point 
there's no real benefit to doing stuff in an in{} block.  And since most of 
the contents of the in{} block are asserts which disappear in release mode 
anyway..

But -- if contracts, or at least portions of them, were able to be evaluated 
at _compile time_ on functions (or individual parameters) that take constant 
values, I could see a much larger benefit.

For example, say you have a function that takes a range of integers:

void fork(int x)
in
{
    assert(x >= 0 && x < 20, "x is out of range");
}
body
{
    // use x!
}

If I call fork(30), this is obviously an error, but won't be reported until 
runtime, and even then, *only in debug mode*.

If, instead, the compiler used its CTFE mechanism to interpret the in{} 
block as long as the params to the functions are constants, you'd get a nice 
compile-time message, like:

error calling fork with parameters (30):
    assertion (x >= 0 && x < 20) failed: "x is out of range"

And better yet, this would happen regardless of whether you're compiling in 
debug mode or not.

It seems like a cool idea anyway.  I wonder, in practice, how often the 
compiler would be able to evaluate the contracts, and if it would be of 
practical use.  Or if there would be a better way to do this (like extending 
the new template parameter constraints to functions: "void fork(int x) if(x 
= 0 && x < 20)"). 

Jun 20 2008
next sibling parent reply dennis luehring <dl.soluz gmx.net> writes:
Jarrett Billingsley schrieb:
....
 If I call fork(30), this is obviously an error, but won't be reported until 
 runtime, and even then, *only in debug mode*.
...
 It seems like a cool idea anyway.  I wonder, in practice, how often the 
 compiler would be able to evaluate the contracts, and if it would be of 
 practical use.  Or if there would be a better way to do this (like extending 
 the new template parameter constraints to functions: "void fork(int x) if(x 
  >= 0 && x < 20)"). 

if got the same for assert itselfe why do assert not act like an static assert if the evaluated is a compiletime value something like better_assert( predicat, text ) { static if compile_time_value( predicate ) static assert( predicate, text ) else assert( predicate, text ) } function test( int value ) { better_assert( value == 4 ); // bla bla } test( 5 ); // compiletime error test( non_const_wrong_value_variable ); // runtime assert and this does not realy help in this small testcase but think of the 1000 in/out checks and normal asserts in your projects do realy want to see the problem only when you actualy call a specific function? ciao dennis
Jun 20 2008
parent reply dennis luehring <dl.soluz gmx.net> writes:
dennis luehring schrieb:
  >Jarrett Billingsley schrieb:
  >....
 If I call fork(30), this is obviously an error, but won't be reported 
 until runtime, and even then, *only in debug mode*.
 ...
 It seems like a cool idea anyway.  I wonder, in practice, how often 
 the compiler would be able to evaluate the contracts, and if it would 
 be of practical use.  Or if there would be a better way to do this 
 (like extending the new template parameter constraints to functions: 
 "void fork(int x) if(x  >= 0 && x < 20)"). 

if got the same for assert itselfe why do assert not act like an static assert if the evaluated is a compiletime value something like better_assert( predicat, text ) { static if compile_time_value( predicate ) static assert( predicate, text ) else assert( predicate, text ) } function test( int value ) { better_assert( value == 4 ); // bla bla } test( 5 ); // compiletime error test( non_const_wrong_value_variable ); // runtime assert and this does not realy help in this small testcase but think of the 1000 in/out checks and normal asserts in your projects do realy want to see the problem only when you actualy call a specific function? ciao dennis

and don't want to change assert - maybe another(ouch!) keyword for example "predication" so i and jarett can use predication (or predicate?) as a replacement for assert in in/out contracts and the other positions im currrently using assert
Jun 20 2008
parent Sean Kelly <sean invisibleduck.org> writes:
== Quote from dennis luehring (dl.soluz gmx.net)'s article
 and don't want to change assert - maybe another(ouch!) keyword	for
 example "predication"
 so i and jarett can use predication (or predicate?) as a
 replacement for assert in in/out contracts and the other positions im
 currrently using assert

hehe, I use "predicate" in C/C++ for assertion checks that I don't want compiled out in release builds :-) Handy for verifying the return value of functions with side-effects. Sean
Jun 20 2008
prev sibling next sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
Jarrett Billingsley wrote:
 I've found myself not using contracts nearly as much as I'd kind of like to. 
 Mostly because, well, other than a nice syntactic separation between the 
 contracts and the body of the function, there's almost nothing I can do with 
 contracts that can't be done with a debug{} block in the function.
 
 The problem boils down to this: no matter how well-defined I make my 
 contracts, they will never actually be tested until runtime.  At that point 
 there's no real benefit to doing stuff in an in{} block.  And since most of 
 the contents of the in{} block are asserts which disappear in release mode 
 anyway..
 
 But -- if contracts, or at least portions of them, were able to be evaluated 
 at _compile time_ on functions (or individual parameters) that take constant 
 values, I could see a much larger benefit.
 
 For example, say you have a function that takes a range of integers:
 
 void fork(int x)
 in
 {
     assert(x >= 0 && x < 20, "x is out of range");
 }
 body
 {
     // use x!
 }
 
 If I call fork(30), this is obviously an error, but won't be reported until 
 runtime, and even then, *only in debug mode*.
 
 If, instead, the compiler used its CTFE mechanism to interpret the in{} 
 block as long as the params to the functions are constants, you'd get a nice 
 compile-time message, like:
 
 error calling fork with parameters (30):
     assertion (x >= 0 && x < 20) failed: "x is out of range"
 
 And better yet, this would happen regardless of whether you're compiling in 
 debug mode or not.
 
 It seems like a cool idea anyway.  I wonder, in practice, how often the 
 compiler would be able to evaluate the contracts, and if it would be of 
 practical use.  Or if there would be a better way to do this (like extending 
 the new template parameter constraints to functions: "void fork(int x) if(x 
  >= 0 && x < 20)"). 
 
 

Sounds totally unnecessary. if you pass 30 to the above fork you already know that it's not between 0 and 20 (that is if you finished 2nd grade of school). you need help from the compiler when you do not know what x is, i.e. in run-time. I don't see any practical benefits to this outside of meta-programming and there I'd expect the compiler to throw the assert error during compilation. another issue is the removal of asserts in release mode. I'm not sure that asserts should only work in debug mode and personally would prefer to keep them in the released version. this probably should have a compiler flag to control it. I don't like DMD's release and debug modes at all and thing that the user should have a more fine grained control over compilation. GCC allows me to define what kinds of optimizations I want with a flag for each setting instead of a global release mode as defined by Walter. for example what if I'd like to compile my code with contracts and with array bounds checking removed? How do I accomplish that?
Jun 20 2008
next sibling parent dennis luehring <dl.soluz gmx.net> writes:
 Sounds totally unnecessary.
 if you pass 30 to the above fork you already know that it's not between
 0 and 20 (that is if you finished 2nd grade of school). 

just think of x 100 000 lines of code projects - not these 3 lines examples - and think of projects that are growing over years ...
 you need help
 from the compiler when you do not know what x is, i.e. in run-time.
 I don't see any practical benefits to this outside of meta-programming
 and there I'd expect the compiler to throw the assert error during
 compilation.

why should the meta-programming section be more "safe" than normal coding? both sides can benefit
Jun 20 2008
prev sibling parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Yigal Chripun" <yigal100 gmail.com> wrote in message 
news:g3h56u$2evu$1 digitalmars.com...

 Sounds totally unnecessary.
 if you pass 30 to the above fork you already know that it's not between
 0 and 20 (that is if you finished 2nd grade of school). you need help
 from the compiler when you do not know what x is, i.e. in run-time.
 I don't see any practical benefits to this outside of meta-programming
 and there I'd expect the compiler to throw the assert error during
 compilation.

I'm suggesting this from the point of view of someone who writes libraries which have certain input restrictions. I'd rather the errors about the inputs to a function happen at compile time than at some indefinite point in time at runtime.
 another issue is the removal of asserts in release mode. I'm not sure
 that asserts should only work in debug mode and personally would prefer
 to keep them in the released version. this probably should have a
 compiler flag to control it. I don't like DMD's release and debug modes
 at all and thing that the user should have a more fine grained control
 over compilation. GCC allows me to define what kinds of optimizations I
 want with a flag for each setting instead of a global release mode as
 defined by Walter.
 for example what if I'd like to compile my code with contracts and with
 array bounds checking removed? How do I accomplish that?

Totally agree. It's funny -- DMDFE already has fine-grained control for contracts, asserts, bounds checking etc. but for some reason those options aren't exposed by the command-line compiler, they're all set as a chunk by -debug and -release.
Jun 20 2008
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Jarrett Billingsley wrote:
 I've found myself not using contracts nearly as much as I'd kind of like to. 
 Mostly because, well, other than a nice syntactic separation between the 
 contracts and the body of the function, there's almost nothing I can do with 
 contracts that can't be done with a debug{} block in the function.
 
 The problem boils down to this: no matter how well-defined I make my 
 contracts, they will never actually be tested until runtime.  At that point 
 there's no real benefit to doing stuff in an in{} block.  And since most of 
 the contents of the in{} block are asserts which disappear in release mode 
 anyway..
 
 But -- if contracts, or at least portions of them, were able to be evaluated 
 at _compile time_ on functions (or individual parameters) that take constant 
 values, I could see a much larger benefit.
 
 For example, say you have a function that takes a range of integers:
 
 void fork(int x)
 in
 {
     assert(x >= 0 && x < 20, "x is out of range");
 }
 body
 {
     // use x!
 }
 
 If I call fork(30), this is obviously an error, but won't be reported until 
 runtime, and even then, *only in debug mode*.
 
 If, instead, the compiler used its CTFE mechanism to interpret the in{} 
 block as long as the params to the functions are constants, you'd get a nice 
 compile-time message, like:
 
 error calling fork with parameters (30):
     assertion (x >= 0 && x < 20) failed: "x is out of range"
 
 And better yet, this would happen regardless of whether you're compiling in 
 debug mode or not.
 
 It seems like a cool idea anyway.  I wonder, in practice, how often the 
 compiler would be able to evaluate the contracts, and if it would be of 
 practical use.  Or if there would be a better way to do this (like extending 
 the new template parameter constraints to functions: "void fork(int x) if(x 
  >= 0 && x < 20)"). 
 
 

It would be nice, yes, but I think having a contract inference & analysis engine that could find (in compile-time) contract violations on any more than just trivial cases would take quite a lot of effort to implement. (so that would be too much for Walter alone) -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jul 26 2008