Recent

Author Topic: functional IF  (Read 3007 times)

mika

  • Full Member
  • ***
  • Posts: 124
Re: functional IF
« Reply #30 on: June 12, 2025, 09:50:37 am »
Was bored and wanted to see how long it takes ...

... and took 15 minutes to create the attached patch for fpc main

Get bored for yet another 15 minutes and create proper merger request in gitlab.

MarkMLl

  • Hero Member
  • *****
  • Posts: 8453
Re: functional IF
« Reply #31 on: June 12, 2025, 10:10:15 am »
Get bored for yet another 15 minutes and create proper merger request in gitlab.

How about *you* spend five minutes reading the entire thread?

If you did you'd find that not only has this been discussed already, but that in the past no less a personage than Sven added the facility to the compiler but for some reason it was slapped down by the rest of the team.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

mika

  • Full Member
  • ***
  • Posts: 124
Re: functional IF
« Reply #32 on: June 12, 2025, 10:38:30 am »
How about *you* spend five minutes reading the entire thread?
Deserved. I limit my time on forum to couple hours in week. Have to be selective what to read. I do skip a lot and missed important comments.

Warfley's solution looks promising. Sad to hear that it wont see daylight.

Warfley

  • Hero Member
  • *****
  • Posts: 1930
Re: functional IF
« Reply #33 on: June 12, 2025, 10:37:12 pm »
While I'm not that fond of if (littering the code with branches doesn't sound very helpful, unless it is known that the conditions follow a pattern at runtime and aren't random) it looks like a way to save a little bit of typing.
It's actually not necessarily about saving typing, it's about using expressions instead of statements. Expressions give more guarantees than statements. Expressions can't be empty, meaning a bug such as:
Code: Pascal  [Select][+][-]
  1. if condition then
  2.   a := Value1
  3. else; // note the ; which directly terminates the else branch
  4.   a := Value2;
which did happen to me more than once, cannot happen with expressions, as every single branch needs to return a value.
Also you can make sure that your variable will always be initialized, which is not necessarily the case for statements:
Code: Pascal  [Select][+][-]
  1. if condition then
  2.   a := 42;
  3. // What happens on else branch?

But I must admit in Pascal it does not have as much of an advantage as in other languages where there are readonly/constant variables. Take C:
Code: C  [Select][+][-]
  1. const int i = a<b ? a : b;
Here the language enforces that i can, because it's const, only be assigned once. It's a very neat property, it's nice for the optimizer, but more importantly, it can avoid bugs when you accidentally use the wrong variable identifier. Rule of thumb: make as much const as possible. But this is only possible if you can assign a value within a single expression, which is why there ternary if-then-else is important.
In pascal there are no such constants, so the usefulness of ternary if-then-else is a bit more restricted

Quote
I'd rather have a way to ensure that the compiler uses branchless code (conditional moves) when I know that the condition is quite unpredictable... At the moment for simple integer values that involves writing a branchless test that continues with using bitwise operations (with 0 or -1) or multiplications (with 0 or 1).
I actually disagree. The FPC team is way to small to be able to get the optimizations anywhere close to what C, C++ or Rust provide, so even if they would spend all of their time adding low level optimizations such as that, using one of the other languages would still be preferable if you really need to write high performance software (also note that those languages have a language design that aids optimization, while Pascal does quite the opposite in many situations).
So instead of trying to be a worse version than C, the language should rather be optimized in areas where it already is good, to make easy to write and maintain programs that are safe and avoid bugs. Even if they aren't the fastest.

creaothceann

  • Full Member
  • ***
  • Posts: 141
Re: functional IF
« Reply #34 on: June 13, 2025, 01:09:43 pm »
Also you can make sure that your variable will always be initialized, which is not necessarily the case for statements:
Code: Pascal  [Select][+][-]
  1. if condition then
  2.   a := 42;
  3. // What happens on else branch?

But I must admit in Pascal it does not have as much of an advantage as in other languages where there are readonly/constant variables. Take C:
Code: C  [Select][+][-]
  1. const int i = a<b ? a : b;
Here the language enforces that i can, because it's const, only be assigned once. It's a very neat property, it's nice for the optimizer, but more importantly, it can avoid bugs when you accidentally use the wrong variable identifier. Rule of thumb: make as much const as possible. But this is only possible if you can assign a value within a single expression, which is why there ternary if-then-else is important.
In pascal there are no such constants, so the usefulness of ternary if-then-else is a bit more restricted
Technically you could use const function parameters, but it'd be too much effort imo to write an entire function just for that...

PascalDragon

  • Hero Member
  • *****
  • Posts: 6035
  • Compiler Developer
Re: functional IF
« Reply #35 on: June 13, 2025, 10:23:55 pm »
If you did you'd find that not only has this been discussed already, but that in the past no less a personage than Sven added the facility to the compiler but for some reason it was slapped down by the rest of the team.

I think one of the main issues was that the approach with the intrinsic added some other magic function with untypical behavior (like Write that can take multiple parameters), so the approach with an if-expression is better.

Creating the patch was the easy part, if I wanted to make a merge request it would take at least double the time to make test cases for it lol

And without test cases it would for sure not be added. ;)

But aside from that, I'm not happy with it, because it's a bit weird to have this for if but not for other statements. What about case?
Code: Pascal  [Select][+][-]
  1. s := case state of
  2.   0: 'Foo';
  3.   1: 'Bar';
  4.   2: 'Baz';
  5.   else 'FooBar';
  6. end;

I can definitely see a case for a case-expression (pun intended). ;)

Or what about try-except:
Code: Pascal  [Select][+][-]
  1. s := try functionThatCanFail() except 'Error';
And when try-except is in there, what about raise:
Code: Pascal  [Select][+][-]
  1. s := try functionThatCanFail() except on E: ESomeException do 'Bar' except raise;
Also useful for incomplete cases:
Code: Pascal  [Select][+][-]
  1. s := case State of
  2. 1: 'Foo';
  3. 2: 'Bar';
  4. else raise EUnknownState.Create(state);
  5. end;

I don't know whether a tryexcept expression would be a good idea, but a no-go would be the use of raise in there, because raise is a statement and the parts of the try ... except will have to be expressions.

Also I'd add them behind a modeswitch (ExpressionStatements?), so that they can be easily disabled for modes like ISO or TP.

Warfley

  • Hero Member
  • *****
  • Posts: 1930
Re: functional IF
« Reply #36 on: June 14, 2025, 01:29:36 am »
I think one of the main issues was that the approach with the intrinsic added some other magic function with untypical behavior (like Write that can take multiple parameters), so the approach with an if-expression is better.

And without test cases it would for sure not be added. ;)
Create a merge request with some tests here: https://gitlab.com/freepascal.org/fpc/source/-/merge_requests/1037

I don't know whether a tryexcept expression would be a good idea, but a no-go would be the use of raise in there, because raise is a statement and the parts of the try ... except will have to be expressions.

Also I'd add them behind a modeswitch (ExpressionStatements?), so that they can be easily disabled for modes like ISO or TP.
Well I found that sometimes you want to embed exception based code into return value based code, and then having something like this would be useful. I omitted the raise part (mostly because its way more complicated than I have time for), and I put it behind a switch "StatementExpressions".

Also unlike my previous patch, I do not add new functions copying the parsing of the statements, I just added an "expression mode" to the existing statement parsing functions, as copying the code for if is simple enough, but with case and try I did not want to redo all that work and introduce new bugs

Thaddy

  • Hero Member
  • *****
  • Posts: 17414
  • Ceterum censeo Trumpum esse delendum (Tnx Charlie)
Re: functional IF
« Reply #37 on: June 14, 2025, 07:33:09 am »
Those exception-expressions look ugly to me.
but r:=if (0 > 1) then a else b and the case expression look very good.
I think there is possibly less issues if the latter two could be committed and keep the former a bit longer open for discussion. Maybe split the merge request in two?
The tests went as expected.
« Last Edit: June 14, 2025, 07:34:42 am by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

Thaddy

  • Hero Member
  • *****
  • Posts: 17414
  • Ceterum censeo Trumpum esse delendum (Tnx Charlie)
Re: functional IF
« Reply #38 on: June 14, 2025, 03:15:13 pm »
@Warfely

OFF TOPIC but do you want to have a go at:
Code: Pascal  [Select][+][-]
  1.    generic function<T:ordinal>;
So type restrictions expanded for ordinal types?
I know my suggestion was more or less approved by PascalDragon. (with a possibly/probably)
( there are several misinformed discussions by forum readers to be found here on the forum about that, ignore these, not on the developers mailing list, though )

This would restrict a generic specialization to any ordinal type, which is very useful.
It is in the same category as <T:class> or <T:record>
It should depend on the ordinal range of the bitness instead.
When I tried to do this myself I ran into trouble with 32 bit vs 64 bit true ordinals and gave up.
At one point I chose to let it depend on bitness alone and that was probably the wrong choice.
It is already listed as  FR for quite some time.
« Last Edit: June 14, 2025, 03:35:25 pm by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

Warfley

  • Hero Member
  • *****
  • Posts: 1930
Re: functional IF
« Reply #39 on: June 15, 2025, 12:06:22 am »
Nah, as I said already, I have a whole list of Merge Requests that I would need to follow up on if I had the time. I just did this because it was easy, just a few hours, but even that was more time than I had available already.
That said, it would be definitely useful to have something like this

jamie

  • Hero Member
  • *****
  • Posts: 6991
Re: functional IF
« Reply #40 on: June 15, 2025, 05:49:30 pm »
Nah, as I said already, I have a whole list of Merge Requests that I would need to follow up on if I had the time. I just did this because it was easy, just a few hours, but even that was more time than I had available already.
That said, it would be definitely useful to have something like this

I know you are having fun dangling that caret with a big smile on your face, but please I am almost out of breath panting waiting for that feature! :D
The only true wisdom is knowing you know nothing

Fibonacci

  • Hero Member
  • *****
  • Posts: 786
  • Internal Error Hunter
Re: functional IF
« Reply #41 on: June 15, 2025, 06:04:50 pm »
I am almost out of breath panting waiting for that feature! :D

Chill, it wont get merged. Its already been rejected, at least in its current form. If it ever gets accepted, it will have to be nerfed first to make sure FPC doesnt accidentally become too competitive with Delphi. Most likely it will be reduced to just a bare "if" (current proposal is much more than that) in the form of an intrinsic "IfThen". Give it a few years, we'll see.

MarkMLl

  • Hero Member
  • *****
  • Posts: 8453
Re: functional IF
« Reply #42 on: June 15, 2025, 06:06:46 pm »
I had something just yesterday which cried out for it: a point in a drawing which for cosmetic reasons had to be moved either upwards or downwards depending on where it was relative to some other point.

Now, I know that I could have done that by subtracting, applying the Sign() function, and multiplying the result by the fudge distance required. But on the balance, keeping things readable mandated an if-then-else statement with almost-identical expressions in the two legs.

All of which also made me wonder about efficiency: in principle at least the less-readable implementation using Sign() should be more efficient since there would be fewer jumps and pipeline stalls.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

munair

  • Hero Member
  • *****
  • Posts: 884
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: functional IF
« Reply #43 on: June 16, 2025, 07:37:37 am »
Was bored and wanted to see how long it takes to build something like that:
Code: Pascal  [Select][+][-]
  1. var
  2.   s: string;
  3. begin
  4.   s := if 0 < 1 then 'Foo' else 'Bar';
  5.   WriteLn(s);
  6. end.

I've been working on something similar for the SharpBASIC compiler, but instead of using IF in an expression, a LET statement is used allowing for one or more 'on' branches:

Code: Text  [Select][+][-]
  1. let s
  2.   on x < y is "Foo";
  3.   on x > y is "Bar" else "Nothing";
  4. end
  5. print(s);

It differs from a case/switch block in that it's purely an assignment statement. This construct clearly separates assignment from boolean evaluation.
« Last Edit: June 16, 2025, 08:27:13 am by munair »
It's only logical.

Thaddy

  • Hero Member
  • *****
  • Posts: 17414
  • Ceterum censeo Trumpum esse delendum (Tnx Charlie)
Re: functional IF
« Reply #44 on: June 16, 2025, 09:04:57 am »
@munair

I am repeating myself, but really, Warfely's suggestion is better than the already (main) possible:
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$modeswitch implicitfunctionspecialization}
  2. {$if fpc_fullversion < 30301}{$error this version requires fpc331 or higher}{$ifend}
  3.  
  4. uses sysutils; // contains generic ifthen for some years.
  5. begin
  6.   writeln(ifthen(0 > 1,'Must be true', 'Suppose it is false'));
  7.   writeln(ifthen(0 < 1,'Must be true', 'Suppose it is false'));
  8.   writeln(ifthen(0 > 1,true, false));
  9.   writeln(ifthen(0 < 1,true, false));
  10. end.

Try leaving out the {$modeswitch implicitfunctionspecialization}
With due respect, you do not seem to fully grasp what the discussion is about.
It is about making such things a compiler intrinsic. Whatever syntax is decided.

For most people, though, the above will suffice.
The modeswitch will likely disappear in a real FPC 4.0 release. (because it will be added to at least mode delphi)

I wrote the generic ifthen. not a big feat, Implicit specialization was not possible at the time.
Good example of how the language features evolve over time and solutions are mutually beneficial.
« Last Edit: June 16, 2025, 10:59:53 am by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

 

TinyPortal © 2005-2018