Function request: Class contracts - default require and ensure for type aliases

Follow up on Function request: Class contracts - require on class level

Take the following type Alias:

type Percentage = double;

In this case I’d like to require that the value is always between 0 and 100, and ensure that it stays between those values.

Proposed syntax:

type Percentage = double
  requires
    self.IsNaN = false;
    0 <= self <= 100;
  ensure
    0 <= self <= 100;
end;

Edit: an example procedure:

method x(p: Percentage);
begin
  //Some code
end;

During compile, the require is added by the compiler, based on the require definition of the alias type:

method x(p: Percentage);
//Require section is added by the compiler based on the definition in the Alias
Require
    p.IsNaN = false;
    0 <= p <= 100;
begin
  //Some code
end;
  • When the type is used as a const or byval parameter for a method, the require that is defined for the type is added to the method for that parameter.
  • When the type is used as an var or out parameter for a method, the require and ensure that are defined for the type are added to the method for that parameter.
  • when the type is used as return value for a method, only the ensure is added to the method for the return value.
  • when the type is used as a field or property in a class, the ensure is added as private invariant for the field.
1 Like

Hmm, on an alias? who would enforce that and how and where?

Hi Marc.
Some more explanation needed I think.

First: I use aliases to make the code more readable; by example var Weight: Kg; instead of var Weight Double; This way I just use a double, but it is completely clear that it represents a Kg.

Second: Each unit or object type that I use this way (as an alias) has it’s own specific properties; by example, the Kg can not be negative as we did not invent anti-gravity yet.

Third: To prevent bugs and flaws it is handy to enforce those specific properties; when an object of type Kg gets a negative value it means there is a bug in the code - And I want to know that as soon as possible.

Fourth: Enforcement during debug, without performance impact on the release build is done using the class contracts. parameters are checked using require, output parameters and return values are checked using ensure and class fields are checked using invariants.

Fifth: Rules and requirements can change. If I program those checks on each and every method and class that use a specific alias, I have to check and change them all when a rule or requirement changes - with a big chance that some are forgotten.

Those points together gave me the idea to centralize the checks.
The alias definition itself defines the checks - this is very readable and maintainable; it is very clear what rules are valid for the specific alias and when rules or requirements change they can be changed at one place.

These rules (require and ensure) on the alias level are only compiler hints to generate the appropriate checks in all methods and classes that use this specific alias during compile time.

Hope it is more clear now

2 Likes

I like the Idea, these could be a awesome extension to Elements.

1 Like

right. i just don’t see how this would be technically feasible. at runtime, Kg is just a double. you can pass it to any system function that processes (and manipulates) doubles. who would enforce the invariant?

1 Like

The checks are only in own code.
If used as parameter in own code, the require checks it before it is used in the routine.
If used as output (parameter or returnvalue) in own code, ensure checks if it is stiil valid before leaving the routine. If a system function within that method did not obey my rules, ensure will fail my routine.
If used as class field, private invariant will check if the field is still valid after a method call in that class.

So it does not matter what a method does - system functions can do what they want, because it is all checked within my own code, at entry and - if needed - at exit.

1 Like

The whole point is that all require and ensure sections of a method is generated based on the type of the used parameters and return value, and private invariant sections are generated based on the type of the used fields in the class.

This means that if I have a class with 10 fields of type Kg, 40 methods with a total of 60 parameters of type Kg and 20 return values of Kg would require me to write:

  • 10 private invariant rules
  • 60 require rules
  • 20 ensure rules
    instead of
  • 1 require rule
  • 1 ensure rule

And when a require rule changes, I have to change it on 60 places instead of 1 place.

And maintain it in 1 place is readable, understandable, maintainable and less error-prone.

Edit: But indeed, when Kg is used as local variable in within a function, nothing will be checked or ensured.

1 Like

Maybe another extension to the method check: Maybe to allow the local checks, the Compiler could create i think silently from this:

type Percentage = double
requires
 not self.IsNaN;
 0 <= self <= 100;
ensure
  0 <= self <= 100;
end;

this:

type
  Percentage = record Extension(Double)
    method require: bool;
    begin
       result :=  not self.IsNaN and
                     0 <= self<= 100;
    end;

    method ensure: bool;
    begin
       result := 0 <= self<= 100;
    end;        
  end;

and now I think the Compiler can treat it as wished, I think if im not wrong here.

So the require is called at the beginning when the Compiler detects the first use of the local KG, and the ensure is called when the Compiler detects the last use of the local KG.

in my fantasie this should be like:

method DoWork;
begin
  var lKG: Percentage := 24;  
 //now the Compiler calls silently lkg.require !   <code-injection>

 writeln(lgk);

 //now the Compiler calls silently lg.ensure !    <code-injection>
end;

This is how it works wit class contracts in the require and ensure sections. :smile:

ah ^^

but cant this behaviour be “copied” into this Scenario ?

:face_with_monocle::face_with_monocle: or just be reused for this Purpose?

Problem with your solution is that this is injected in Debug aswell in Release builds. And that will give performance problems.
My request was to use the already available class contract constructs - all tests available during development, debug and test phase, but absolutely no load on the release build, ensuring performance for the customers.

I actually cant see why my method implies a specific-build-mode?

they could also implement it internally like: “if defined(‘Debug’) then else doNothing” or am I missunderstanding you ??

I really do not understand why you don’t want to use the class contracts, as this is what it is designed for …

? Just for sure, my original post was About this line here from you:

why you would have brought up this line if it is being solved from the ClassContracts?

This was just to be clear to @mh. Letting him know that I understand this limitation.

I got that :smiley:

but again, feel free to call me stupid, but that is what my Purpose was actually for, to solve this Limitation?.
You can inject the Code just in debug mode (internally by the Compiler I mean) and then you can have your orignal function request + it works now when you use local variables?

So this (theortically) would solve this.

so you would have working: class contracts for type aliases as Parameters/out vars and local variables

But it makes it about 1000% more complex for the compiler guys to build it and it adds almost nothing. If you don’t do anything with a local variable (I mean calling any of your own implementations), it won’t be that important.

Hi Theo,
why not something like:

type
  CheckedPercentage = public sealed class
    public
      property Value : Double read private write;  // to avoid changing the value externally.
    // define what you want to check, with the operators, ...
  end;

  {$if Debug}
  Percentage = CheckedPercentage;
  {$else}
  Percentage = Double;
  {$endif}

It could be viable, depending on your use.

And if I use the type in 20 classes?
It’s not that it can not be done, it’s that I want the checks at one central place in the code.

If you use Percentage in other classes, you’ve nothing more to do than define CheckedPercentage correctly…