Recent

Author Topic: IfThen function  (Read 6757 times)

RayoGlauco

  • Full Member
  • ***
  • Posts: 179
  • Beers: 1567
Re: IfThen function
« Reply #15 on: April 28, 2022, 01:18:34 pm »
Just a side comment: if you're avoiding dividing by zero, wouldn't it be better to compare like this:

Code: Pascal  [Select][+][-]
  1. if d<>0 then ...
  2. // or maybe this...
  3. if Abs(d)>0.0001 then ...
To err is human, but to really mess things up, you need a computer.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5481
  • Compiler Developer
Re: IfThen function
« Reply #16 on: April 28, 2022, 01:24:09 pm »
As you correctly pointed out: IfThen is a Function, thusly all Parameters get thrown on the stack, consequencing in being evaluated before pushed on the stack.
Yes, IfThen has the "inline" modifier, but do we know if that Function in OP's case really got inlined? Does he have {$inline on}? (No Idea if for math.pp it's switched on or not)

It doesn't matter if the function is inlined or not, because the parameters will always be evaluated, cause they might have side effects (an exception is one) and inlining must not make these side effects disappear.

I wasn't in the least intending any criticism of the development team there. It was more a gentle acknowledgement of the fact that anybody who tinkered might risk incurring Sven's wrath :-)

It's mainly Florian who tinkers with that. ;)


 IfThen--Conditional function that returns one of two values.
The whole point of these functions (or ternary operators) is that only one value is evaluated.
This would be a speed up operation with a safety check.
variable = condition ? answer1 : answer2 in C
or
variable=iif(condition,answer1, answer2) in basic.
Evaluating both answers seems pointless.

IfThen is an ordinary function, not a ternary operator or a ternary operator alike. And for an ordinary function the rules of calling a function apply.

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: IfThen function
« Reply #17 on: April 28, 2022, 01:40:44 pm »
EDIT: i'll never understand people using such functions instead of doing it yourself, and catching everything that can go wrong beforehand
What's wrong with doing it yourself instead?
It's the age old discussion of statements vs expressions, which exists since LISP was created. An if-then-else expression like the python one:
Code: Pascal  [Select][+][-]
  1. x = TrueValue if Condition else FalseValue
is a single expression, meaning it is one "line" that results in a value.
The if-then-else statement:
Code: Pascal  [Select][+][-]
  1. if Condition then x := TrueValue else x := FalseValue;
Is a composite of multiple statments:
Code: Pascal  [Select][+][-]
  1. if Condition then
  2.   x := TrueValue // True assignment statement
  3. else
  4.   x := FalseValue; // False Assignment statement
There are of course some differences that are easy to spot. For example renaming the variable x requires one change in the expression while 2 changes in the statement. On the other hand in the if-then-else statment you can use non-expressions:
Code: Pascal  [Select][+][-]
  1. if EOF(file) then
  2.   x := '';
  3. else
  4.   ReadLn(file, x); // not an expression, but a statement
which is simply not possible for an expression.

Another thing, which might not be so important for such a very simple case, but with more complex expressions/statements is that for expressions x will always be assigned a value, while in the statement case, you could forgett the else case, or even forgett doing the assignment in the else case:
Code: Pascal  [Select][+][-]
  1. if Condition then
  2.   x := Value;
  3. // if condition is false x is undefined
So an if-then-else expression guarantees that the variable will be assigned a value, no matter what, while in the statement case, you need to hope that the programmer that wrote it did not make a mistake, forgott to add the else case, mistyped the variable name, etc.
It also gives you a visual hint on what is happening. The first thing you see with the expression is "x :=" so you know that after this line x will have a value. With the statement the first line is "if condition" so you need to look into both the if and else case to figure out what is happening here. So this also makes it easier to understand what the code is actually doing

Then going a step further from the example at hand, expressions can be used in places where statements cant, e.g. as function parameter:
Code: Pascal  [Select][+][-]
  1. func(d/n if n>0 else 0);
While for statements, you need to create a temporary variable to evaluate the result in:
Code: Pascal  [Select][+][-]
  1. if n>0 then
  2.   tmp := d/n
  3. else
  4.   tmp := 0;
  5. func(tmp);
or create two calls to the function
Code: Pascal  [Select][+][-]
  1. if n>0 then
  2.   func(d/n)
  3. else
  4.   func(0);
But now if anything about the function changes, you need to update two places (and have the chance to forgett to update about 2 places and therefore introduce bugs)

Lastly, this is a bit of a soft point, as this is of course highly dependent on the circumstances, expressions have a somewhat limit on their possible complexity. After all, everything needs to fit into a single expression. And this forces the programmer to write more concisely. This is why programs written in expression based languages, like the aforementioned LISP tend to be much smaller than their statement based counterparts (the ballpark numbers that float around academic literature are usually a factor 10-20 less code for the same functionality). And this keeps your code better maintainable.

Generally it is simply less typing you have to do:
Code: Pascal  [Select][+][-]
  1. x := TrueVal if Condtion else FalseVal; // python syntax
  2. // vs
  3. if Condition then x := TrueVal else x := FalseVal; // 2 times x := instead of only one

So I think that when you can use expressions, you should. It helps to avoid errors by always being well defined and helps to write more concise code.
« Last Edit: April 28, 2022, 01:56:58 pm by Warfley »

MarkMLl

  • Hero Member
  • *****
  • Posts: 6686
Re: IfThen function
« Reply #18 on: April 28, 2022, 02:05:32 pm »
variable=iif(condition,answer1, answer2) in basic.
Evaluating both answers seems pointless.

If it looks like a function, it should behave like a function.

If it doesn't look like a function then it should be properly documented.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: IfThen function
« Reply #19 on: April 28, 2022, 02:13:23 pm »
If it looks like a function, it should behave like a function.

If it doesn't look like a function then it should be properly documented.

MarkMLl
This is something I also don’t like about the internal functions, for example concat for strings, looks like a function, is documented like a function, but it’s not a function, so e.g. getting the pointer to it will not work

MarkMLl

  • Hero Member
  • *****
  • Posts: 6686
Re: IfThen function
« Reply #20 on: April 28, 2022, 02:17:39 pm »
It's mainly Florian who tinkers with that. ;)

I prefer not living dangerously :-)

I've found the relevant bit of code in the compiler, and speculated in the past as to whether it /could/ be usefully tweaked... without, I'd stress, suggesting that it /should/ be tweaked. However Marco's comment about the current value having been arrived at heuristically does make me wonder whether it really is optimal for all possible architectures or whether e.g. CPU pipeline length ought to be taken into account.

Apropos the current discussion, you'll note that I'm not mounted on my favourite hobbyhorse of having an if-then-else expression converted to an if-then-else statement by some form of preprocessor. My opinion is that the expression form /should/ be in the core language, if for no reason other than that there was no obvious reason for its having been lost in the ALGOL-Pascal transition. But I'm pretty sure you worked that one out years ago :-)

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Zvoni

  • Hero Member
  • *****
  • Posts: 2327
Re: IfThen function
« Reply #21 on: April 28, 2022, 02:24:06 pm »
If it looks like a function, it should behave like a function.

If it doesn't look like a function then it should be properly documented.

MarkMLl

If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck
 :D :D :D :D
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

Zoran

  • Hero Member
  • *****
  • Posts: 1830
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: IfThen function
« Reply #22 on: April 28, 2022, 02:28:18 pm »
I'm more surprised he's not getting an error on Linux.
Different settings for compiler?


On Linux, you probably tried it with gtk2 library, then the explanation is here: https://wiki.freepascal.org/Multiplatform_Programming_Guide#Gtk2_and_masking_FPU_exceptions

MarkMLl

  • Hero Member
  • *****
  • Posts: 6686
Re: IfThen function
« Reply #23 on: April 28, 2022, 02:29:09 pm »
If it looks like a function, it should behave like a function.

If it doesn't look like a function then it should be properly documented.

MarkMLl
This is something I also don’t like about the internal functions, for example concat for strings, looks like a function, is documented like a function, but it’s not a function, so e.g. getting the pointer to it will not work

That's true, but at least that's something that the compiler can- and does- handle correctly as an error. Allowing a function to decide which (if any) of its parameters should be evaluated really would open a can of worms, and I refuse to be mollified by somebody's coming along and claiming that since a modern editor can highlight the irregularity everything's fine.

Apropos Concat(), I admit to almost always using its equivalent + operator. However as Pascal gains more overloading and dynamic array capabilities I'd be far happier if something like a _ operator were defined to (also) mean concatenation, so that it was possible to override + meaning "add these arrays" and _ meaning "concatenate these arrays". But I know that's not going to happen.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: IfThen function
« Reply #24 on: April 28, 2022, 03:23:32 pm »
Allowing a function to decide which (if any) of its parameters should be evaluated really would open a can of worms, and I refuse to be mollified by somebody's coming along and claiming that since a modern editor can highlight the irregularity everything's fine.
There are languages which allow this, I think the most prevalent one of those is probably Haskell, which has so called lazy evaluation. Basically there any values are only evaluated when they are used. This is actually extremely neat and allows for a lot of really cool stuff (e.g. partial evaluation of infinite recursion). The thing is, this requires a special form of programming and only works if your language is build around that.
One major thing why it works in Haskell is because Haskell is side-effect free. So any call to a function if it gets the same input parameters will aways produce the same output parameters. Therefore the execution order, or if something is executed at all, does not matter. This is not the case for Pascal, here an expression can have side effects, for example:
Code: Pascal  [Select][+][-]
  1. var x: Integer = 0;
  2. function foo: Integer;
  3. begin
  4.   Result := 42;
  5.   Inc(x);
  6. end;
  7.  
  8. procedure Bar(a, B: Integer);
  9. ...
  10. Bar(foo, foo);
  11. WriteLn(x);
Here both foos return 42, but in the background a global variable is incremented, so the writeln is dependent on how many foos where executed.

There are "hacks" used sometimes in non pure languages (i.e. languages with side effects) that allow this, this usually boils down to putting your computation in an anonymous function that is passed and then evaluated only if needed, so a python like hack could look like this:
Code: Pascal  [Select][+][-]
  1. def ifThenElse(condition: bool, trueFunc: Function[T, []], falseFunc: Function[T, []]) -> T:
  2.   if condition:
  3.     return trueFunc()
  4.   else:
  5.     return falseFunc()
  6.  
  7. # usage
  8. ifThenElse(n>0, lambda: n/d, 0);
But this requires the user to know and basically promise that this will not have any side effects, because otherwise the correctness of the function cannot be ensured.

So when anonymous functions are a thing, if anyone wants that they can use this hack, but I think it is best for a eager language (i.e. left most innermost evaluation of parameters) like pascal to stay eager because otherwise this would probably introduce more problems than solutions

MarkMLl

  • Hero Member
  • *****
  • Posts: 6686
Re: IfThen function
« Reply #25 on: April 28, 2022, 04:06:34 pm »
The thing is, this requires a special form of programming and only works if your language is build around that.

And more importantly, it requires the user to know what he's letting himself in for.

Considering ALGOL derivatives, I /have/ used some where there were pragmata which changed the calling convention at the point where a function was defined, and where that information wasn't available as metadata where it was called... but that was very much intended for systems programming work, and it was highly likely that a program where parameter passing and saved registers didn't match would abend in short order.

Taking a language which isn't newly-defined with thoroughly-documented foibles and obfuscating the evaluation criteria would be a recipe for disaster. And Pascal already has lazy evaluation which is quite enough of a problem.

Having said which... I /suppose/ that IfThen() taking a boolean and a couple of (anonymous?) functions could be written to only invoke one of them, since that would conform to the "spirit of Pascal" to the same extent as a sorting routine which takes a comparison function as a parameter. But compared with the concise ALGOL-style conditional it would be needlessly florid IMO.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: IfThen function
« Reply #26 on: April 28, 2022, 04:55:46 pm »
I mean half of whats needed is already there:
Code: Pascal  [Select][+][-]
  1.   generic TFunction<T> = function: T;
  2.   generic TLazyParam<T> = record
  3.   public type
  4.     TEvalFunction = specialize TFunction<T>;
  5.   private
  6.     FHasValue: Boolean;
  7.     FValue: T;
  8.     FFunc: TEvalFunction;
  9.   public
  10.     function Value: T; inline;
  11.     class operator := (AFunc: TEvalFunction): specialize TLazyParam<T>;
  12.     class operator:=(const AValue: T): specialize TLazyParam<T>;
  13.   end;
  14.  
  15. function TLazyParam.Value: T;
  16. begin
  17.   if not FHasValue then
  18.   begin
  19.     FHasValue := True;
  20.     FValue := FFunc();
  21.   end;
  22.   Result := FValue;
  23. end;
  24.  
  25. class operator TLazyParam.:=(AFunc: TEvalFunction): specialize TLazyParam<T>;
  26. begin
  27.   Result.FHasValue := False;
  28.   Result.FFunc := AFunc;
  29. end;
  30.  
  31. class operator TLazyParam.:=(const AValue: T): specialize TLazyParam<T>;
  32. begin
  33.   Result.FHasValue := True;
  34.   Result.FValue := AValue;
  35. end;
  36.  
  37. function LazyIfThenElse(Condition: Boolean; TrueValue, FalseValue: specialize TLazyParam<Double>): Double; inline;
  38. begin
  39.   If Condition then
  40.     Result := TrueValue.Value
  41.   else
  42.     Result := FalseValue.Value;
  43. end;

With anonymous functions this could be used like this (I am using python style anonymous function syntax, because my brain refuses to give any kind of justification to the abomination that is the Delphi anonymous function syntax):
Code: Pascal  [Select][+][-]
  1. x := LazyIfThenElse(n > 0, lambda: n/d, 0);

This would be kinda cool. And if you want it even prettier, you could use a macro:
Code: Pascal  [Select][+][-]
  1. {$define lazy:=lambda:}
  2. x := LazyIfThenElse(n > 0, lazy n/d, 0);
Sadly with delphi style anonymous functions this will be the most ugly construct imaginable
Code: Pascal  [Select][+][-]
  1. x := LazyIfThenElse(n > 0, function: Double begin Result := n/d; end, 0);
Whoever at embarcadero thought that this syntax is a good idea should be banned from touching a PC ever again
« Last Edit: April 28, 2022, 05:01:51 pm by Warfley »

RayoGlauco

  • Full Member
  • ***
  • Posts: 179
  • Beers: 1567
Re: IfThen function
« Reply #27 on: April 28, 2022, 07:43:47 pm »
I find this topic very interesting, from a technical point of view. Regarding the problem initially raised, I think that a structure if ... then ... else ... is much clearer and simpler.
To err is human, but to really mess things up, you need a computer.

Чебурашка

  • Hero Member
  • *****
  • Posts: 568
  • СЛАВА УКРАЇНІ! / Slava Ukraïni!
Re: IfThen function
« Reply #28 on: April 29, 2022, 08:26:44 am »
I was looking for something compact to represent things like:

Code: Pascal  [Select][+][-]
  1. if condition then
  2. begin
  3.   x := expression_to_be_evaluated_if condition_is_true;
  4. end
  5. else begin
  6.   x := expression_to_be_evaluated_if condition_is_false;
  7. end;
  8.  
simply because I had many of them in a row and the code appearance matters to me (mostly for sake of ease of maintenance/extensibility rather than beauty).

Of course IfThen does not do that job, my head was only partially thinking in "free pascal". Part of it was still thinking in C with 

Code: C  [Select][+][-]
  1. x = condition ? true_expr : false_expr;  
  2.  

or macros like the one I wrote above.

Maybe this could be a necessity that others have in common with me though. If so I am happy to have brought this to your attention, even if because of a mistake.

« Last Edit: April 29, 2022, 08:32:55 am by tt »
FPC 3.2.0/Lazarus 2.0.10+dfsg-4+b2 on Debian 11.5
FPC 3.2.2/Lazarus 2.2.0 on Windows 10 Pro 21H2

MarkMLl

  • Hero Member
  • *****
  • Posts: 6686
Re: IfThen function
« Reply #29 on: April 29, 2022, 08:45:53 am »
or macros like the one I wrote above.

As I've already pointed out, FPC's macros don't take parameters. Your example in #6 won't work.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

 

TinyPortal © 2005-2018