Jump to content
David Schwartz

handling predicate conditions

Recommended Posts

What's the easiest way you know of to test predicate conditions of input parameters on methods and display a meaningful error message when one fails and skipping the rest, without using exceptions?

 

Common situations include:

 

* an input value is NIL or not

* an input string is empty or not

* an input numeric is < 0, = 0, or > 0

* an input object has a property like the above that includes that the object's reference pointer is valid

 

I'm curious if there's a way to have a list of one-liners that are easy to read and aren't part of a huge nested if..then statement filled with and's or or's and terminated with a  begin ShowMessage(...); Exit; end;

Edited by David Schwartz

Share this post


Link to post
48 minutes ago, David Heffernan said:

Best is to use exceptions of course

Well, aside from a general sense that exceptions shouldn't be used for flow control ... 

 

Anyway, I'm looking for some examples as well. This is not a topic that is very widely discussed, although for lots of projects I've worked on, they've devised some kind of consistent approach, usually NOT based on exceptions. But many haven't, so you can see personality differences in the code depending whose favorite method for vetting incoming parameters was used.

 

This isn't very Delphi-specific, so discussions about it in other languages would be fun to read, too.

Edited by David Schwartz

Share this post


Link to post

Ok, if we are talking other languages: best is to not be even able to compile the code or get warnings when the static code analysis cannot make sure that these conditions are met - for example using non nullable reference types.

Share this post


Link to post

This isn't using exceptions for control flow. It's using exceptions to deal with invalid parameters.

 

If you don't use exceptions, and you show a message dialog to the user then what is code going to do next? How are you going to avoid following the normal execution path. You can exit this function. But what about the caller? And its caller? And its caller? And what about when you run your code through your program's API rather than interactively. Pretty debilitating to show a dialog to an API user. 

Share this post


Link to post
33 minutes ago, David Heffernan said:

This isn't using exceptions for control flow. It's using exceptions to deal with invalid parameters.

 

If you don't use exceptions, and you show a message dialog to the user then what is code going to do next? How are you going to avoid following the normal execution path. You can exit this function. But what about the caller? And its caller? And its caller? And what about when you run your code through your program's API rather than interactively. Pretty debilitating to show a dialog to an API user. 

Ahh, well ... I'm working on a form-based app, and the code I'm staring at isn't of the nature you're describing. But I see your point, for sure.

 

Again, I'm looking for some examples, because the few I've seen don't take things like this into consideration.

 

Historically speaking, these things get lumped into "error logging" mechanisms. If you're working with a server that has no display, then that's a valid perspective. But not so much in a form-based app -- you want to tell the user to fix the problem so processing can continue. Even in situations where the client-side code ends up passing improperly filtered data to the back-end, there needs to be a way to a message from the back-end that the front-end can then use to alert the user so the problem can be corrected.

 

I don't see this much in desktop apps, even using DB servers. But boy do I see it a lot in web-based scenarios where a simple data entry error in one field causes the entire form to be reset and all of the previous data to be lost.

 

(I was trying to enter some data into a site that asks for some work history, starting with the most recent / current thing. I added a couple then realized I forgot my "current" status. There's no way to enter something anywhere but at the end. After several attempts at moving things down, I just deleted everything and started over. Apparently there's a bug where if you check the box that says you're "currently employed" there, the back-end won't accept that, and it deletes the entire (first) entry, which is why i couldn't move things down. After deleting everything, it just left me with an empty list and no error message.)

 

I see this same kind of problem repeatedly in web-based scenarios, and no consistent way of handling it. All these fancy frameworks and nobody seems to have a well thought-out way of handing these kinds of exceptions.

Share this post


Link to post
2 hours ago, Stefan Glienke said:

Ok, if we are talking other languages: best is to not be even able to compile the code or get warnings when the static code analysis cannot make sure that these conditions are met - for example using non nullable reference types.

No, I'm thinking about run-time errors here. Like, you cannot proceed without entering something in a Name field.

 

You could say it's "required field" handling, but the form follows one set of rules, the back-end might follow one or two others, and they're all independent of each other. If you decide to change a field from required to options, or vice versa, you have to implement that logic in several different places, and it's not always obvious where they are. And it's hard to test for. 

Share this post


Link to post

It still says exceptions to me. 

 

But you say that you don't want to use exceptions. Can you explain why you don't want to use exceptions? 

Share this post


Link to post

To get a sophisticated answer you need to actually describe your situation properly and not just say "invalid values" - invalid in what context?

 

Restricting UI from entering any invalid values can go from controls that don't even allow it, show a marker that their value is wrong, not allowing clicking ok/next to showing an "following fields have invalid values..." dialog.

Declaring guard clauses in code for functions and methods that restrict passing any invalid input is a different thing and in that case I would always go with exceptions of a certain kind (EInvalidArgument or similar) either handwritten or by using some guard clause helper. If the programming language allows I would push this further by restricting the parameter types to only allow what is valid and for instance use decicated domain types instead of for example type string for an IP address but a dedicated TIPAddress type.

  • Like 1

Share this post


Link to post
5 minutes ago, DiGi said:

Are you asking for something like https://fluentvalidation.net/? I don't know about similar Delphi framework

Any approach like this in Delphi would be impossible or suck (because it would need to be stringly typed) because Delphi does not have lambda expressions and generic extension methods.

  • Like 1

Share this post


Link to post
15 hours ago, David Schwartz said:

Well, aside from a general sense that exceptions shouldn't be used for flow control ... 

 

Anyway, I'm looking for some examples as well. This is not a topic that is very widely discussed, although for lots of projects I've worked on, they've devised some kind of consistent approach, usually NOT based on exceptions. But many haven't, so you can see personality differences in the code depending whose favorite method for vetting incoming parameters was used.

 

This isn't very Delphi-specific, so discussions about it in other languages would be fun to read, too.

 

I usually test for pre and postconditions like this:

 

procedure TBlobDB.CreateNewDatabase(aDBStream, aIndexStream: TStream;
    aTakeStreamOwnership: boolean);
const
  Procname = 'TBlobDB.CreateNewDatabase';
begin
  if not Assigned(aDBStream) then
    raise EParameterCannotBeNil.Create(Procname,'aDBStream');
  if not Assigned(aIndexStream) then
    raise EParameterCannotBeNil.Create(Procname,'aIndexStream');

This is for errors that are supposed to be found and fixed during the testing phase or (for library code) in unit tests. For user input the input is validated before it is used by the program, if this is at all feasable. The end user should never see an exception coming from parameter validation at a lower level, in my opinion, since the UI context may not be evident at that point and it may be unclear to the user what he did wrong.

 

  • Like 3

Share this post


Link to post
6 hours ago, PeterBelow said:

 

I usually test for pre and postconditions like this:

 


procedure TBlobDB.CreateNewDatabase(aDBStream, aIndexStream: TStream;
    aTakeStreamOwnership: boolean);
const
  Procname = 'TBlobDB.CreateNewDatabase';
begin
  if not Assigned(aDBStream) then
    raise EParameterCannotBeNil.Create(Procname,'aDBStream');
  if not Assigned(aIndexStream) then
    raise EParameterCannotBeNil.Create(Procname,'aIndexStream');

This is for errors that are supposed to be found and fixed during the testing phase or (for library code) in unit tests. For user input the input is validated before it is used by the program, if this is at all feasable. The end user should never see an exception coming from parameter validation at a lower level, in my opinion, since the UI context may not be evident at that point and it may be unclear to the user what he did wrong.

 

Why even bother writing those checks? An exception will be raised as soon as you attempt to use the nil instances. 

Share this post


Link to post
Guest
6 minutes ago, David Heffernan said:

Why even bother writing those checks? An exception will be raised as soon as you attempt to use the nil instances. 

Because of fail fast

Share this post


Link to post
24 minutes ago, Schokohase said:

Because of fail fast

Reasonable at certain interfaces between modules, but not in every single function. 

Share this post


Link to post
On 8/5/2019 at 11:14 PM, David Schwartz said:

What's the easiest way you know of to test predicate conditions of input parameters on methods and display a meaningful error message when one fails and skipping the rest, without using exceptions?

 

Common situations include:

 

* an input value is NIL or not

* an input string is empty or not

* an input numeric is < 0, = 0, or > 0

* an input object has a property like the above that includes that the object's reference pointer is valid

 

I'm curious if there's a way to have a list of one-liners that are easy to read and aren't part of a huge nested if..then statement filled with and's or or's and terminated with a  begin ShowMessage(...); Exit; end;

With these requirements the solution may be something like:

    Validator
    .Check(value <> nil,  'Value is nil!')
    .Check(str <> '',     'String is empty!')
    .Check(Value.Num > 0, 'Value.Number <= 0!');

Check() receives a boolean to check and string message to complain.

If boolean is true returns Self. If false - other instance whose Check() method just returns Self on any input.

Share this post


Link to post
3 hours ago, Georgge Bakh said:

With these requirements the solution may be something like:


    Validator
    .Check(value <> nil,  'Value is nil!')
    .Check(str <> '',     'String is empty!')
    .Check(Value.Num > 0, 'Value.Number <= 0!');

Check() receives a boolean to check and string message to complain.

If boolean is true returns Self. If false - other instance whose Check() method just returns Self on any input.

Ahh, and example! Thanks!

 

So how do you bail out of the procedure if a check fails? Does this throw an exception? Or is another test needed?

 

@David Heffernan I'm actually fairly agnostic about Exceptions, It's just that they can be a PITA at times.

Edited by David Schwartz

Share this post


Link to post

You guys are all really great theoreticians. I'd really much rather see a variety of examples than debate theory. That's because I see very little written about this topic anywhere.

 

 

Share this post


Link to post

Examples aren't much use without understanding the rationale behind them. If exceptions are a PITA you must be doing something wrong. It's error handling without exceptions that is a PITA. 

Share this post


Link to post
Guest
5 hours ago, David Schwartz said:

You guys are all really great theoreticians. I'd really much rather see a variety of examples than debate theory. That's because I see very little written about this topic anywhere.

 

 

We can only be theoretically/abstract because you asked a theoretically/abstract question: 

21 hours ago, Stefan Glienke said:

To get a sophisticated answer you need to actually describe your situation properly and not just say "invalid values" - invalid in what context?

 

Share this post


Link to post
12 hours ago, David Heffernan said:

Why even bother writing those checks? An exception will be raised as soon as you attempt to use the nil instances. 

Because I can set up my IDE to skip EParameterCannotBeNil exceptions in the debugger, while still catching other nil pointers.

Share this post


Link to post
11 hours ago, Georgge Bakh said:

With these requirements the solution may be something like:


    Validator
    .Check(value <> nil,  'Value is nil!')
    .Check(str <> '',     'String is empty!')
    .Check(Value.Num > 0, 'Value.Number <= 0!');

Check() receives a boolean to check and string message to complain.

If boolean is true returns Self. If false - other instance whose Check() method just returns Self on any input.

It is almost the same as

Assert(value <> nil,  'Value is nil!');
Assert(str <> '',     'String is empty!');
Assert(Value.Num > 0, 'Value.Number <= 0!');

 

Share this post


Link to post
54 minutes ago, Uwe Raabe said:

Because I can set up my IDE to skip EParameterCannotBeNil exceptions in the debugger, while still catching other nil pointers.

Every function you write that accepts a reference type, you check that it is assigned on entry to the function? 

Share this post


Link to post
Guest
16 minutes ago, David Heffernan said:

Every function you write that accepts a reference type, you check that it is assigned on entry to the function?

If it is public available? - Yes.

If it is only private - No (or very rare).

Share this post


Link to post
13 hours ago, David Heffernan said:

Why even bother writing those checks? An exception will be raised as soon as you attempt to use the nil instances. 

Not true - this ain't .NET or Java that raise an NPE as soon as you reference any member - it can very well call non virtual methods and then eventually raise some AV which you then need to decipher (everyone knows what an access violation at 0x0071f2a7: read of address 0x000000f7 means, right?)

Edited by Stefan Glienke

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×