Jump to content
Marco Cantu

Inline Variables Coming in 10.3

Recommended Posts

Could someone please explain the benefit of type inference in a programming language that showcases type safety? As soon as the compiler has to guess a type, and my be wrong, this could result in C-like problems later on. I have made my peace with inline variables (yes, I am quick at giving up), but this feature seems a bit too "over the top" bait for non Delphi developers. I mean, this too can be ignored, but with a team big enough, it just find it's way into code nevertheless.

Share this post


Link to post
9 minutes ago, Sherlock said:

the compiler has to guess a type

Well not really guessing. The compiler uses the expression type, which is deterministic. And you can still add a type cast if you are not certain...

  • Like 1
  • Thanks 1

Share this post


Link to post

The benefit of type inference is that you don't have to state the obvious. If you put the result of let's say GetCustomers into a variable it can explicitly type that variable from the type that GetCustomers returns, if that is a TList<TCustomer>, a TCustomerList or something else it usually does not matter. It also might make refactoring more stable because if you might change that function to return a more special or a more broad type then there is a high chance that the code will still compile and work.

Share this post


Link to post
3 minutes ago, Stefan Glienke said:

 It also might make refactoring more stable because if you might change that function to return a more special or a more broad type then there is a high chance that the code will still compile and work.

Ooooh! That actually makes sense and sounds sweet! I take back everything I said.

Share this post


Link to post

This is truly great and long missed feature. Truly great. 

 

But... I hate buts... there are things to watch out. Namely, type inference breaks reference counting. It is one of those don't mix objects and interfaces pitfalls. Should I say that this works perfectly on ARC compiler 🙂

 

If you have reference counted class, you cannot rely on type inference, you have to specify the interface type.

 

// broken
var Instance := TInterfacedObject.Create;

// not broken
var Instance: IInterface := TInterfacedObject.Create;

 

  • Like 1

Share this post


Link to post
13 minutes ago, Dalija Prasnikar said:

If you have reference counted class, you cannot rely on type inference, you have to specify the interface type.

That happens if you look at C# and do not have Delphi in mind.

 

Remember IEnumerable/IEnumerable<T>?

Share this post


Link to post
1 hour ago, Dalija Prasnikar said:

var Instance := TInterfacedObject.Create;

What's wrong with this code? It should create and return an object of TInterfacedObject type, isn't it?

Well, it should be freed after use: 

Instance.Free;

 

Share this post


Link to post
8 minutes ago, Kryvich said:

What's wrong with this code?

The issue is that it is ambiguous. Usually you store reference counted objects as interface reference, not as object reference. If you pass that variable into some method that triggers reference counting your object might be prematurely destroyed and you run into an error once you hit the Free call.

 

Imo the compiler should help you here to avoid coding errors and warn about this inline declaration potentially being wrong (guessing either way might be wrong) when inferring the type.

Edited by Stefan Glienke
  • Thanks 1

Share this post


Link to post
1 hour ago, Dalija Prasnikar said:

This is truly great and long missed feature. Truly great. 

 

But... I hate buts... there are things to watch out. Namely, type inference breaks reference counting. It is one of those don't mix objects and interfaces pitfalls. Should I say that this works perfectly on ARC compiler 🙂

 

If you have reference counted class, you cannot rely on type inference, you have to specify the interface type.

 


// broken
var Instance := TInterfacedObject.Create;

// not broken
var Instance: IInterface := TInterfacedObject.Create;

 

This was already the case with normal var declarations. Obviously if you want the var to be a different type than the R-value then you'll have to declare that type.

In your own code you can fix this by declaring class functions like so:

 

type
  TMyClass = class(TInterfacedObject, IMyIntf)
  public
    class function CreateIntf: IMyInf;
  end;

...

  class function TMyClass.CreateIntf: IMyInf;
  begin
    Result:= TMyClass.Create;
  end;

//Now type inference works just fine.
//Alternatively perhaps typecasting works?

var IMyVar := IMyInf(TMyClass.Create);

 

Edited by Johan Bontes
  • Like 1

Share this post


Link to post
11 minutes ago, Markus Kinzler said:

The type is missing and has to be guessed.

No the type does not have to be guessed, it is a `TInterfacedObject`. As is stated in the create statement.

 

What would be great though is to have static methods for interfaces:

 

class function IMyInterface.Create: IMyInterface; static;
//or even better
constructor IMyInterface.Create;

Obviously internally the constructor would translate to a static class function returning the interface.

We can already do this by wrapping interfaces inside records and using implicit operator overloads, but that's a lot of typing.

There is nothing stopping the compiler from helping us here, it's just a bit of syntactic sugar.

Then we can just write

 

var IMyInf:= IMyInterface.Create;

Just like we can do in Java 8. (Yes, I dislike java just as much as everyone else, but static interface methods are a pretty cool feature.)

Edited by Johan Bontes

Share this post


Link to post
8 minutes ago, Johan Bontes said:

What would be great though is to have static methods for interfaces

Useless in this case because then the implementation of the interface method would have to know one particular implementation of that interface already.

Share this post


Link to post
8 minutes ago, Markus Kinzler said:

The type created is sure, but not the type of the variable

 


var Instance

What type does the variable will have here?

var Instance := TObject.Create;

or here?

var Instance := TFoo.Create;

Ok, now you can answer what variable type we have on

var Instance := TInterfacedObject.Create;

because the rules are not changing

 

PS: TObject, TFoo and TInterfacedObject

Edited by Schokohase

Share this post


Link to post
6 minutes ago, Stefan Glienke said:

Useless in this case because then the implementation of the interface method would have to know one particular implementation of that interface already.

Ah, yes obviously!

Share this post


Link to post
17 minutes ago, Johan Bontes said:

 


constructor IMyInterface.Create;

Obviously internally the constructor would translate to a static class function returning the interface.

 

Then the interface needs to know about the default class that implements it. Get circular references between units.

Share this post


Link to post
1 hour ago, Johan Bontes said:

This was already the case with normal var declarations. Obviously if you want the var to be a different type than the R-value then you'll have to declare that type.

 

But with normal var declarations you are forced to explicitly write the type for the variable. In this case, you can easily forget that you have to explicitly declare variable as interface and rely on type inference which will get it "wrong".

Share this post


Link to post

@Marco Cantu Do you have any info on how inline variables affect code optimization?

In larger methods registers are going out quickly.

Does it help if some variables are declared deeper in those methods, or it's pre-parsed and codegen is the same as before? 

Share this post


Link to post

I think inline vars and type inference will be especially usefull in functional programming with Delphi.

Now we write like this:

program TestClosure;
{$APPTYPE CONSOLE}
uses
  SysUtils;
var
  Proc: TProc;
begin
  Proc := (function: TProc
    var
      n: Integer;
    begin
      n := 0;
      Result := procedure begin
        Write(n);
        Inc(n);
      end;
    end)();
  Proc;
  Proc;
  Proc;
  Proc;
  Readln
end.

In the new Delphi 10.3 Rio, this can probably be reduced to

program TestClosure10_3;
{$APPTYPE CONSOLE}
uses
  SysUtils;
begin
  var Proc := (function: TProc begin
    var n := 0;
    Exit(procedure begin
      Write(n);
      Inc(n);
    end);
  end)();
  Proc;
  Proc;
  Proc;
  Proc;
  Readln
end.

BTW can you say what this code will display?

Similar JavaScript code for comparison:

var func=(function(){
  var n = 0;
  return function(){
    console.log(n);
    n++;
  }
})();
func();
func();
func();
func();

 

Edited by Kryvich
Removing unnecessary formatting

Share this post


Link to post

Another observation. From the very beginning, we had this method of declaring procedures and functions:

procedure procedureName(parameterList);
  localDeclarations;
begin
  statements
end;

The reserved word begin marks the beginning of the procedure body. But starting from 10.3 local variables can be declared directly in the procedure body. Therefore, there is no need to save begin for procedures and functions:

procedure procedureName(parameterList);
  localDeclarations;
  statements
end;

 

Share this post


Link to post
10 minutes ago, Kryvich said:

Therefore, there is no need to save begin for procedures and functions:

...rendering any currently working Delphi parser useless...

  • Thanks 2

Share this post


Link to post
1 hour ago, Kryvich said:

functional programming with Delphi

A few closures are not functional programming 🙂 Even less so if they are not pure.

Yes, you can borrow a fraction of concepts from FP but you cannot do FP in a non FP language.

Edited by Stefan Glienke

Share this post


Link to post
40 minutes ago, Uwe Raabe said:

...rendering any currently working Delphi parser useless...

I am sure that plenty of 3rd party tools will fall apart at first with 10.3 when they encounter a var or const at places they are not expecting them 😉

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

×