[fpc-pascal] Optional param modifier

Martin Frb lazarus at mfriebe.de
Sun Apr 14 23:48:44 CEST 2019


On 14/04/2019 22:08, Ryan Joseph wrote:
>
> Per my example, currently we know that GetThing() COULD return nil so unless we know what the function does by reading the documentation or looking at the implementation we need to test for nil. Right? I suggest the modifier simply to enforce that check and let it be known that’s the desired usage.
I do get what you want. But not why (the personal why I get, the 
objective one not).
Or not what sense is it makes... Where again I get your description, but 
it seems arbitrary/randomly picked.
Sure perfectly fine on the few examples you gave. But then by that 
measure others will bring their examples (and believe me they will, and 
it will be examples neither of us would be able to derive today). And 
then we need further rules/modifiers/dedicated-checks for whatever 
examples they bring up...

As you wrote: "we need to test for nil"
Yes, and we need to do that for all of them. Even if they are not marked 
as "expect-me-to-be-nil". Because even if they are not marked, they 
still can be.

That means if we start warning in selected cases (the ones that are 
marked), then we give the user **false security** over all other cases. 
(I did not get a warning, so why does it crash on a nil-deref?)

The only place where we do not need to check for nil, is if we know it 
can not be nil.
We can gain that knowledge from 1 of 2 places
- some sort of modifier to the type declaration - or some sort of 
class-contract
- the docs

We can NOT get it from code review. Code may change, and then all the 
depended code becomes wrong. (It may be that the code should return nil, 
but due to a bug the current implementation does not)

> So, in other words:
>
> 	function FindThing: TThing; optional;
>
> means you MUST check for nil. If the value is nil that’s not an error. “optional” is probably a bad name for this I know.
Don't worry about the name.

How does the compiler know that I have checked. Code can be arbitrarily 
complex. There will always be code that does check, but the compiler can 
not tell (heck I may have a checkAssigned procedure in assembler).
So at best the compiler can warn during compilation, but never give an 
error.

> 	function MakeThing: TThing;
>
> this means maybe check for nil or maybe not check for nil. We don’t know what the programmer intended because no extra information was given (at least in the declaration). Because of the name we probably assume this is going to return a new object which is safe so checking for nil would be a waste.
What if it run out of memory? And catches the exception.
Checking for nil is never a waste. Unless you have proof (in the 
mathematical sense) that nil can not happen.
Or if your rules allow you to trust the docs, and the docs say you can 
relay on it.

>   But we don’t know that except for what the name implies or if we read the code.
Neither name, nor implementation are a reliable source for that info. 
See above.

> Is that more clear? If there’s a better way to express this then please let me know.
Its been perfectly clear from (almost) the start.

I admit the contract/assert stuff may have made it seem different. But 
that is because this approach does not hold up (IMHO). And therefore the 
closest alternative was presented.

In the very end it is to avoid crashing on nil de-ref. And the 
contract/assert can help there. Though in a completely different way.


> For parameters I like what Sven suggested to have a “requires” section 
> in functions to check this but wouldn’t it make sense if the function 
> parameter gave some hint also? “can be nil” or “can’t be nil” would 
> both be helpful in terms of documentation and compile time errors 
> instead of runtime errors. 

The pre-condition (requires) would have the same amount of compile time 
checks (and the remainder runtime) as a keyword like notnil or maybenil. 
In the same way range checks and others go part compile, part runtime.

As for "documentation". I disagree with the way it is done in oxygen. 
But I am not sure I have any good alternative.
For me a class contract (require/ensure) is part of the interface.

So it would have to be like (and similar for plain procedures, no class) 
// can be all on one line....

type
   TFoo= class
      function DoTheFooThing(Foo1, Foo2: TFoo): Tbar;
         requires
            assigned(Foo1) or assigned(Foo2): 'Need at least 1 foo, for 
the connection';
         guarantees // better than ensure
            assigned(result);
            result.KnowsMyFoo = true: 'Connection exists';  // the =true 
is redundant
     procedure DoTheBar;
   end;

So reading this declaration, you immediately know what is valid. And if 
custom messages are give, they may tell you way

It is to be noted, that requires and guarantees contain conditions 
(expressions), not pascal code. So technically it is ok, that they are 
in the interface and not implementation.
If I want something in the implementation, I can use normal "assert" 
(which by the way, have often great documenting value)





More information about the fpc-pascal mailing list