www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Feature request: Optional, simplified syntax for simple contracts

reply "TommiT" <tommitissari hotmail.com> writes:
"Simple things should be simple, complex things should be 
possible." -Alan Kay

I'd like to simplify the syntax of function pre- and 
post-conditions when the contract block consists of a single 
assert statement. A special syntax for this special case would 
omit all of the following:
1) the block's curly braces
2) the assert keyword
3) the semi-colon ending the assert statement
4) the body keyword (if and only if it follows right after the 
block)

So, instead of writing this:

int func(int i)
in
{
     assert(i < 5);
}
out(r)
{
     assert(r < 9);
}
body
{
     return i * 2;
}

...you'd be able to write this:

int func(int i)
in (i < 5)
out(r) (r < 9)
{
     return i * 2;
}
Jun 15 2013
next sibling parent reply Manu <turkeyman gmail.com> writes:
Super awesome idea! How about coma separated expressions to perform
multiple asserts?

int func(int i, int j) in(i<5, j<10)
{
  return i + j;
}


On 16 June 2013 07:45, TommiT <tommitissari hotmail.com> wrote:

 "Simple things should be simple, complex things should be possible." -Alan
 Kay

 I'd like to simplify the syntax of function pre- and post-conditions when
 the contract block consists of a single assert statement. A special syntax
 for this special case would omit all of the following:
 1) the block's curly braces
 2) the assert keyword
 3) the semi-colon ending the assert statement
 4) the body keyword (if and only if it follows right after the block)

 So, instead of writing this:

 int func(int i)
 in
 {
     assert(i < 5);
 }
 out(r)
 {
     assert(r < 9);
 }
 body
 {
     return i * 2;
 }

 ...you'd be able to write this:

 int func(int i)
 in (i < 5)
 out(r) (r < 9)
 {
     return i * 2;
 }
Jun 15 2013
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 06/16/2013 02:19 AM, Manu wrote:
 Super awesome idea! How about coma separated expressions to perform
 multiple asserts?

 int func(int i, int j) in(i<5, j<10)
 {
    return i + j;
 }
...
Use &&.
Jun 15 2013
parent reply Lionello Lunesu <lionello lunesu.remove.com> writes:
On 6/16/13 8:42, Timon Gehr wrote:
 On 06/16/2013 02:19 AM, Manu wrote:
 Super awesome idea! How about coma separated expressions to perform
 multiple asserts?

 int func(int i, int j) in(i<5, j<10)
 {
    return i + j;
 }
 ...
Use &&.
Not the same. && won't tell you which one failed.
Jun 17 2013
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 06/18/2013 08:52 AM, Lionello Lunesu wrote:
 On 6/16/13 8:42, Timon Gehr wrote:
 On 06/16/2013 02:19 AM, Manu wrote:
 Super awesome idea! How about coma separated expressions to perform
 multiple asserts?

 int func(int i, int j) in(i<5, j<10)
 {
    return i + j;
 }
 ...
Use &&.
Not the same. && won't tell you which one failed.
QoI issue.
Jun 20 2013
prev sibling next sibling parent reply "TommiT" <tommitissari hotmail.com> writes:
On Sunday, 16 June 2013 at 00:19:37 UTC, Manu wrote:
 Super awesome idea! How about coma separated expressions to 
 perform
 multiple asserts?

 int func(int i, int j) in(i<5, j<10)
 {
   return i + j;
 }
Do you mean ...to get more specific error messages than with in(i<5 && j<10) ?
Jun 15 2013
next sibling parent Marco Leise <Marco.Leise gmx.de> writes:
Am Sun, 16 Jun 2013 04:22:59 +0200
schrieb "TommiT" <tommitissari hotmail.com>:

 On Sunday, 16 June 2013 at 00:19:37 UTC, Manu wrote:
 Super awesome idea! How about coma separated expressions to 
 perform
 multiple asserts?

 int func(int i, int j) in(i<5, j<10)
 {
   return i + j;
 }
Do you mean ...to get more specific error messages than with in(i<5 && j<10) ?
Actually these are 2 great ideas. Make that expand to: in { assert(i<5, __FUNCTION__ ~ ": precondition i<5 failed. Variables: i == " ~ to!string(i)); assert(j<10, __FUNCTION__ ~ ": precondition j<10 failed. Variables: j == " ~ to!string(j)); } -- Marco
Jun 16 2013
prev sibling parent reply Manu <turkeyman gmail.com> writes:
On 16 June 2013 12:22, TommiT <tommitissari hotmail.com> wrote:

 On Sunday, 16 June 2013 at 00:19:37 UTC, Manu wrote:

 Super awesome idea! How about coma separated expressions to perform
 multiple asserts?

 int func(int i, int j) in(i<5, j<10)
 {
   return i + j;
 }
Do you mean ...to get more specific error messages than with in(i<5 && j<10) ?
Error messages would be more useful, and it may be much easier for the compiler/optimiser to use this information in the future as separated out into distinct expressions.
Jun 17 2013
parent "TommiT" <tommitissari hotmail.com> writes:
On Tuesday, 18 June 2013 at 02:38:28 UTC, Manu wrote:
 On 16 June 2013 12:22, TommiT <tommitissari hotmail.com> wrote:

 On Sunday, 16 June 2013 at 00:19:37 UTC, Manu wrote:

 Super awesome idea! How about coma separated expressions to 
 perform
 multiple asserts?

 int func(int i, int j) in(i<5, j<10)
 {
   return i + j;
 }
Do you mean ...to get more specific error messages than with in(i<5 && j<10) ?
Error messages would be more useful, and it may be much easier for the compiler/optimiser to use this information in the future as separated out into distinct expressions.
I don't really know about this stuff, but since assert and enforce are built-in things, why couldn't they be smart enough to tell me which ones of the sub-expressions returned false. a = 4; b = false; c = 11; d = true; assert(a < 5 && (b || c < 10) && d); AssertError: { true && (false || false) && ? } It doesn't know the last expression because it didn't evaluate it.
Jun 18 2013
prev sibling parent reply "Aleksandar Ruzicic" <aleksandar ruzicic.info> writes:
On Sunday, 16 June 2013 at 00:19:37 UTC, Manu wrote:
 Super awesome idea! How about coma separated expressions to 
 perform
 multiple asserts?

 int func(int i, int j) in(i<5, j<10)
 {
   return i + j;
 }
I find use of comma inside of parentheses of a statement a bit unusual. Correct me if I'm wrong but I don't think there is a single statement in D that separates it's "parts" with a comma. It's always a semi-colon. So I think it should be: int func(int i, int j) in (i < 5; j < 10) { return i + j; } But either comma or a semi-colon used as a separator, this is a really nice syntactic sugar!
Jun 17 2013
parent reply Manu <turkeyman gmail.com> writes:
What about the argument list only 3 characters earlier?


On 18 June 2013 15:16, Aleksandar Ruzicic <aleksandar ruzicic.info> wrote:

 On Sunday, 16 June 2013 at 00:19:37 UTC, Manu wrote:

 Super awesome idea! How about coma separated expressions to perform
 multiple asserts?

 int func(int i, int j) in(i<5, j<10)
 {
   return i + j;
 }
I find use of comma inside of parentheses of a statement a bit unusual. Correct me if I'm wrong but I don't think there is a single statement in D that separates it's "parts" with a comma. It's always a semi-colon. So I think it should be: int func(int i, int j) in (i < 5; j < 10) { return i + j; } But either comma or a semi-colon used as a separator, this is a really nice syntactic sugar!
Jun 17 2013
parent reply "Tyler Jameson Little" <beatgammit gmail.com> writes:
Or the comma operator:

int x = (5, 3); // x is 3

Arrays:

int[] x = [3, 5];

Struct initializers:

struct t { int x, y };
auto z = t(3, 5);

Variable declarations:

int x = 5, y = 3;

I'm not sure which would be more idiomatic though... I'm leaning 
more towards commas though, to keep with the syntax of the 
initializers.

On Tuesday, 18 June 2013 at 05:28:07 UTC, Manu wrote:
 What about the argument list only 3 characters earlier?


 On 18 June 2013 15:16, Aleksandar Ruzicic 
 <aleksandar ruzicic.info> wrote:

 On Sunday, 16 June 2013 at 00:19:37 UTC, Manu wrote:

 Super awesome idea! How about coma separated expressions to 
 perform
 multiple asserts?

 int func(int i, int j) in(i<5, j<10)
 {
   return i + j;
 }
I find use of comma inside of parentheses of a statement a bit unusual. Correct me if I'm wrong but I don't think there is a single statement in D that separates it's "parts" with a comma. It's always a semi-colon. So I think it should be: int func(int i, int j) in (i < 5; j < 10) { return i + j; } But either comma or a semi-colon used as a separator, this is a really nice syntactic sugar!
Jun 17 2013
parent reply "Aleksandar Ruzicic" <aleksandar ruzicic.info> writes:
Well, that's why I've said we don't have a statement that uses 
comma to separate it's part. We have lists (argument list, 
initializer list, array literals, etc) and I would keep comma for 
separating list items.

In for and foreach we use semi-colons, so I tought that using 
semi-colon in in would be more consistent than comma.

But that's just my opinion and if this feature got implemented I 
would be happy with commas also. :)


On Tuesday, 18 June 2013 at 05:46:35 UTC, Tyler Jameson Little 
wrote:
 Or the comma operator:

 int x = (5, 3); // x is 3

 Arrays:

 int[] x = [3, 5];

 Struct initializers:

 struct t { int x, y };
 auto z = t(3, 5);

 Variable declarations:

 int x = 5, y = 3;

 I'm not sure which would be more idiomatic though... I'm 
 leaning more towards commas though, to keep with the syntax of 
 the initializers.

 On Tuesday, 18 June 2013 at 05:28:07 UTC, Manu wrote:
 What about the argument list only 3 characters earlier?


 On 18 June 2013 15:16, Aleksandar Ruzicic 
 <aleksandar ruzicic.info> wrote:

 On Sunday, 16 June 2013 at 00:19:37 UTC, Manu wrote:

 Super awesome idea! How about coma separated expressions to 
 perform
 multiple asserts?

 int func(int i, int j) in(i<5, j<10)
 {
  return i + j;
 }
I find use of comma inside of parentheses of a statement a bit unusual. Correct me if I'm wrong but I don't think there is a single statement in D that separates it's "parts" with a comma. It's always a semi-colon. So I think it should be: int func(int i, int j) in (i < 5; j < 10) { return i + j; } But either comma or a semi-colon used as a separator, this is a really nice syntactic sugar!
Jun 17 2013
parent "TommiT" <tommitissari hotmail.com> writes:
On Tuesday, 18 June 2013 at 06:00:49 UTC, Aleksandar Ruzicic 
wrote:
 Well, that's why I've said we don't have a statement that uses 
 comma to separate it's part. We have lists (argument list, 
 initializer list, array literals, etc) and I would keep comma 
 for separating list items.

 In for and foreach we use semi-colons, so I tought that using 
 semi-colon in in would be more consistent than comma.

 But that's just my opinion and if this feature got implemented 
 I would be happy with commas also. :)
The content inside a simplified pre/post-condition would be a list of runtime boolean expressions, therefore comma is a more logical separator than a semi-colon, which indicates a statement. Semi-colon would invite newbies try to write code inside contracts that looks like it might work but doesn't: void fun(double x) in (double squared = x * x; squared < 10; squared > -2) { } Another list of boolean expressions: bool[] arr = [ true, false, 0, 1, isSomething() ]; ...separated by commas alright.
Jun 18 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
I posted this to bugzilla as an enhancement request:

http://d.puremagic.com/issues/show_bug.cgi?id=10396

Perhaps you could vote for it there if you like it.
Jun 17 2013
prev sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Saturday, 15 June 2013 at 21:45:16 UTC, TommiT wrote:
 "Simple things should be simple, complex things should be 
 possible." -Alan Kay

 I'd like to simplify the syntax of function pre- and 
 post-conditions when the contract block consists of a single 
 assert statement. A special syntax for this special case would 
 omit all of the following:
 1) the block's curly braces
 2) the assert keyword
 3) the semi-colon ending the assert statement
 4) the body keyword (if and only if it follows right after the 
 block)

 So, instead of writing this:

 int func(int i)
 in
 {
     assert(i < 5);
 }
 out(r)
 {
     assert(r < 9);
 }
 body
 {
     return i * 2;
 }

 ...you'd be able to write this:

 int func(int i)
 in (i < 5)
 out(r) (r < 9)
 {
     return i * 2;
 }
I'd rather reserve this kind of syntax for static contract checking or something similar.
Jun 17 2013
parent reply "TommiT" <tommitissari hotmail.com> writes:
On Tuesday, 18 June 2013 at 05:27:57 UTC, deadalnix wrote:
 On Saturday, 15 June 2013 at 21:45:16 UTC, TommiT wrote:
 "Simple things should be simple, complex things should be 
 possible." -Alan Kay

 I'd like to simplify the syntax of function pre- and 
 post-conditions when the contract block consists of a single 
 assert statement. A special syntax for this special case would 
 omit all of the following:
 1) the block's curly braces
 2) the assert keyword
 3) the semi-colon ending the assert statement
 4) the body keyword (if and only if it follows right after the 
 block)

 So, instead of writing this:

 int func(int i)
 in
 {
    assert(i < 5);
 }
 out(r)
 {
    assert(r < 9);
 }
 body
 {
    return i * 2;
 }

 ...you'd be able to write this:

 int func(int i)
 in (i < 5)
 out(r) (r < 9)
 {
    return i * 2;
 }
I'd rather reserve this kind of syntax for static contract checking or something similar.
Isn't signature constraint (the if clause) good enough for all static contract checking purposes?
Jun 17 2013
parent reply "TommiT" <tommitissari hotmail.com> writes:
One small issue is that 'in' and 'if' look quite similar, so the 
compiler should probably issue a warning if you test for a 
compile time constant inside a pre-condition.

void foo(T)(T v)
in (isIntegral!T) // Warning: did you mean 'if'
{ }
Jun 17 2013
parent reply Marco Leise <Marco.Leise gmx.de> writes:
I have implemented in CTFE, what I'd like asserts to look like:
http://dpaste.1azy.net/2ec082c0

It prints at runtime:

"x > y && x < 10" (x must be a digit and larger than y) failed with x: 2, y=
: 3
----------------
<stack trace>


When you write a contract like this:

void foo(uint x, uint y)
in { mixin(q{ x > y && x < 10 }.holds ("x must be a digit and larger than y=
")); }
body { =E2=80=A6 }



-- Marco
Jun 19 2013
parent "TommiT" <tommitissari hotmail.com> writes:
On Wednesday, 19 June 2013 at 19:21:20 UTC, Marco Leise wrote:
 I have implemented in CTFE, what I'd like asserts to look like:
 http://dpaste.1azy.net/2ec082c0

 It prints at runtime:

 "x > y && x < 10" (x must be a digit and larger than y) failed 
 with x: 2, y: 3
 ----------------
 <stack trace>


 When you write a contract like this:

 void foo(uint x, uint y)
 in { mixin(q{ x > y && x < 10 }.holds ("x must be a digit and 
 larger than y")); }
 body { … }



 -- Marco
I think you should make a separate bugzilla enhancement request (or DIP. I don't know which one's more appropriate) for incorporating these better assert messages. It's a separate issue from contracts' syntax after all. I like the fact that we wouldn't need any new (comma-separated-booleans) syntax for asserts then.
Jun 20 2013