Marco Cantu 78 Posted October 29, 2018 Another blog post about coming language changes in 10.3: "Introducing Inline Variables in the Delphi Language" http://blog.marcocantu.com/blog/2018-october-inline-variables-delphi.html 2 Share this post Link to post
Sherlock 663 Posted October 29, 2018 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
Marco Cantu 78 Posted October 29, 2018 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... 1 1 Share this post Link to post
Stefan Glienke 2019 Posted October 29, 2018 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. 1 Share this post Link to post
Sherlock 663 Posted October 29, 2018 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
Dalija Prasnikar 1404 Posted October 29, 2018 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; 1 Share this post Link to post
Guest Posted October 29, 2018 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
Kryvich 165 Posted October 29, 2018 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
Stefan Glienke 2019 Posted October 29, 2018 (edited) 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 October 29, 2018 by Stefan Glienke 1 Share this post Link to post
Johan Bontes 11 Posted October 29, 2018 (edited) 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 October 29, 2018 by Johan Bontes 1 Share this post Link to post
Markus Kinzler 174 Posted October 29, 2018 Quote What's wrong with this code? The type is missing and has to be guessed. Share this post Link to post
Johan Bontes 11 Posted October 29, 2018 (edited) 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 October 29, 2018 by Johan Bontes Share this post Link to post
Stefan Glienke 2019 Posted October 29, 2018 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
Markus Kinzler 174 Posted October 29, 2018 The type created is sure, but not the type of the variable var Instance Share this post Link to post
Guest Posted October 29, 2018 (edited) 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 October 29, 2018 by Guest Share this post Link to post
Johan Bontes 11 Posted October 29, 2018 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
Kryvich 165 Posted October 29, 2018 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
Dalija Prasnikar 1404 Posted October 29, 2018 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
Attila Kovacs 631 Posted October 29, 2018 @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
Kryvich 165 Posted October 29, 2018 (edited) 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 October 29, 2018 by Kryvich Removing unnecessary formatting Share this post Link to post
Kryvich 165 Posted October 29, 2018 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
Uwe Raabe 2064 Posted October 29, 2018 10 minutes ago, Kryvich said: Therefore, there is no need to save begin for procedures and functions: ...rendering any currently working Delphi parser useless... 2 Share this post Link to post
Stefan Glienke 2019 Posted October 29, 2018 (edited) 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 October 29, 2018 by Stefan Glienke Share this post Link to post
Stefan Glienke 2019 Posted October 29, 2018 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