Delphi showing its age?

Referencing @David_Duffy’s thread … System Resources - How Many Is Too Many?

This shows Delphi’s grandma bloomers showing through in the VCL example, imo.


function GetBitmap(Index: Integer; Image: TBitmap): Boolean;

procedure TForm1.Button1Click(Sender: TObject);
begin
   ImageList1.GetBitmap(2,Image1.Picture.Bitmap);
   Image1.Stretch := True;
end;
  • mutable Image (really pointer to Image) passed as a copy parameter
  • boolean return value discarded, …
    :face_with_raised_eyebrow:

Python :

def  GetBitmap(Index):
  return Bitmap, Boolean

image1, error = GetBitmap(2) 

C++

auto GetBitmap(int Index) -> std::tuple< TBitmap, bool >

auto [image1, error] = GetBitmap(2);

Yes I do wish Delphi supported tuple return parameters. I don’t use them much in c# but in typescript I use a similar construct (destructuring).

I have lost all hope of the delphi language ever being modernised.

I sometimes return a Record from a function where I need more than one value returned. Other times I use out params. Depends on the situation. Tuples would be better.

2 Likes

Yes, Records and out parms.

Had to google what a tuple was.

And now I ask why is a language showing it’s age because it doesn’t support some ‘random’ feature of another language?

Now, there, I’m showing my age!

It’s not just some random feature, it’s a really about a bunch of features

This is just a few of them

https://www.finalbuilder.com/resources/blogs/delphi-language-enhancements

1 Like

Well, an out parameter is a bad idea … a bad language design idea, I mean. It’s just a harder to read version of returning multiple values.

Functions ideally should be honest … say exactly what is going in and what is coming out of them … and operate without side effects.

I was taken a bit by surprise by my negative reaction on seeing the code I posted. It’s fairly new for me, but I feel it strongly now.

Having the TImage modified, with no declaration of it in the signature of the function, is not good.
In this case I think even the c++ translation TBitmap* Image1 that emphasises that it is a pointer conveys better information.

Well in regards to the tuple issue, here is my sample code of doing it in delphi @Paul_McGee that you asked about previously and I showed at a previous meeting.

1 Like

Hi Geoff

Why do you need the private type declarations of TA and TB?
Also, even if they are not required, what are they doing?

By the way, I do like your solution.

Regards
Graeme

Looks like they are unneeded.

Not sure how I feel about this style. It’s expressive, fluent, but still feels kinda odd.

Any thoughts on improving it… within the limitations of delphi of course?

Not really. I prefer the return values on the left of the expression. This is a poor man’s tuple.


 TTuple<T1, T2> = record
    constructor Create(a: T1; b: T2);
    Value1 : T1;
    Value2: T2;
  end

function Foo : TTuple<string, integer>;
...

procedure Test;
begin
  var x = Foo();
  ShowMessag(x.Value1);
end;

Of course you would need to define tuple types for the number of parameters you have.

in c# tuples are really simple to use these days (used to have to do the above) - as of c# 7.1 you can do this

(double, int) t1 = (4.5, 3);
Console.WriteLine($"Tuple with elements {t1.Item1} and {t1.Item2}.");
// Output:
// Tuple with elements 4.5 and 3.

(double Sum, int Count) t2 = (4.5, 3);
Console.WriteLine($"Sum of {t2.Count} elements is {t2.Sum}.");
// Output:
// Sum of 3 elements is 4.5.

There’s more with c#, like deconstruction (didn’t know that til today, use it all the time in typescript).

1 Like
  • I like it. :+1:

  • One thing … a category theory / functional look at it might call ‘SendBack’ as ‘Lift’ … ie it is a (bi)functor creating a product type from the two source types.

  • side note, I tried :
    writeln(success.ToString(true) + ' ' + errorMessage);
    but it had to be
    writeln(success.ToString(TUseBoolStrs.True) + ' ' + errorMessage); :smirk:

I added a Tuple with 3 values as well.

A short, recent video on tuples. C++ vs Rust: Tuples - YouTube

I have had Tuples for up to 4 values in Spring for a while now but simply because you cannot deconstruct them right into the nicely named variables on the fly as Vincent showed makes this feature very ugly in Delphi. As always you have to explicitly declare some record types if you want some speaking field names rather than Item1, Item2, and so on.

The best I currently can do is this:

function Foo : Tuple<string, integer>;
begin
end;

begin
  var name: string;
  var count: Integer;
  Foo.Unpack(name, count);
end.

Let’s add this feature request and we could write this:

  Foo.Unpack(var name, var count);

Now we add a new operator overload that does what I do in unpack (C# calls it deconstruct) and we would be there:

var (name, count) := Foo();

Or the other syntaxes that C# allows if that example above looks too alien for a Delphi developer:

(name: string; count: Integer) := Foo;

Oh, hey that almost looks like some record declaration…

1 Like

Except it’s a pain to have to declare record types all over the show for one off calls.

  const [isOpen, setIsOpen] = useState<boolean>(false);

JavasScript/TypeScript calls it destructuring and it’s immensly useful.

There are so many other low hanging fruit that embarcadero are unable to do so I do not hold out any hope of this ever being a thing.

What I was referring to is that almost if not all building blocks to make this an official language feature are already there.

We have the way to create unnamed record types when declaring a variable like this:

var
  x: record name: string; count: Integer end;

But we cannot do that for a return type (we get an E2029 Identifier expected but ‘RECORD’ found)

function Foo: record name: string; count: Integer end;
begin

end;

which would be a bit more verbose than its C# counterpart:

(string, int) Foo()
{
    return ("Hello World", 42);
}

var foo = Foo();
Console.WriteLine(foo.Item1 + " " + foo.Item2);

C# uses the Tuple<string,int> type here which has these Item1… etc fields

I could have given the return type explicit field names though or directly deconstruct into local variables like this:

(var name, var count) = Foo();
Console.WriteLine(name + " " + count);

Now getting back to the verbosity if we had at least the Tuple deconstruction and some builtin Tuple types like .NET has (I think up to 20 or so fields - well personally 4 or so would be good enough, if you need more you can still write your own) you could write this:

function Foo: Tuple<string,Integer>;

and then use the aforementioned deconstruction (that would be the only missing piece currently and personally I think this should simply be done with a new operator overload) - given name and count are already declared (pre inline variables) - I have not decided of what the best syntax would be so I am using parantheses just like C# does which feels ok given we also use these when doing const record declaration:

(name, count) := Foo;

or use inline variables with type inference:

(var name, var count) := Foo;

Since you cannot overload on return type there should be no issue with type inference and possible overloads. Maybe I should really submit this as a feature proposal :innocent:

Go for it. I’m ok with the C# syntax, anything else would be too verbose.

As language constructs go, this doesn’t look to be all that complicated, but having said that, even the simplest suggestions take years to happen :man_shrugging:

We could return functions that take integer indices …