Type inference and inline declarations

Type inference and inline declarations

(Copied from a comment I wrote on Non-tech.)

I would like to see more scope fencing through inline declarations and automatic type inference.

for aItem in myList do if Pos( ‘widget’, aItem ) > 0 then listbox1.Items.Add( aItem ); 

aItem doesn’t really need a declaration if the list is a TList or in any other way expose a type through the default property.

Should I need to cast the type of an iteration variable, it could be done like: for aItem:String in myList – and still be possible to type check at compile time.

I think inline declaration would be very good for iterators – so that you don’t mix variable and iterator use – such as first using it in a for loop iteration and later reuse it as a reference variable.

Scope limited temporary variables would also be great. 

Alt.1

using A:TThisType, B:TThatType

begin

// The simple reference variables A and B only exists within this scope. 

end;

Alt.2

using auto A:TThisType, B:TThatType

begin

// A and B are created on entering and destroyed on exiting this scope. 

end;

And then there is the dreaded with statement which could be replaced by a type inferred reference

using A as Some.Object.List[Index]

begin

  // A only exists within this scope

  A.Value := ..

end

A would be typed from the list element type, and if you need a cast

using A:TSomeType as Some.Object.List[Index]

begin

  // A only exists within this scope

  A.Value := ..

end

That would also be type checkable at compile time.

4 thoughts on “Type inference and inline declarations


  1. On the loop variable, implicit declaration raises some ambiguity if you add the variable in a higher-level scope, similar to the “with” ambiguity.


    You first code “for aItem in …” and then someone add aItem to the parameters of that function, and oops, your code is no longer doing what it should.


    While if you have local scoping and an extra keyword “for var aItem in …” then you make it explicit that you want a new variable for the loop, and then the compiler can react correctly if another aItem gets added higher up (like warn you about ambiguity).


    Also while a loop-scoped loop variable often is what you’re after, there is just no good reason that the loop variable be necessarily undefined after the loop. It can be well-defined, the limitation was in Delphi because of compiler optimization quirks.


    In some cases, it can make much sense to have the loop variable be defined after the loop, such as when you break on an item and want to process that item.


    On the scoping, an imho simpler syntax, no new keyword, and you can declare on multiple lines


    begin


       var A := Some.Object.List[Index];


       var B : TSomeType := …whatever….;


       // A & B only exist within this scope


    end


    which beyond the more finer-grained scoping, is going to be easier to debug since you can easily place breakpoints on the assignations, while for a single-line “using”, you’re not.


  2. I would much prefer inline “var” to “using” as well, as Eric Grange showed above.


    Though I personally would prefer that the for-var-loop variable should only be defined inside the for-loop scope. If you want to keep the element, declare a variable in the outside scope and assign before breaking. Then it’s explicit what you want, and the for-loop variable can be freed as soon as the for-loop exits, instead of lingering until the end of the outer scope.


  3. The ambiguity problem exists today. See sample program below.  Zero hints or warnings.


    Inline declarations could indeed flag the conflict without breaking old code.  


    The inline var declaration would be a usable alternative, but opens up some issues with f.x. try finally/except – should the variables be accessible in finally/except?


    The using would exclude that by definition.


    As for the assignment – and stepping – if you step into, you’d be stepping into both assignments – assuming the values came from functions. If you step over, the values are set and you can inspect them. I don’t think that’s an issue.


    program ScopeAmbiguity;


    {$APPTYPE CONSOLE}


    {$R *.res}


    uses


      System.SysUtils;


    type


      TScope = class


      private


        Fix: Integer;


      public


        constructor Create;


        procedure Test;


        property ix: Integer read Fix write Fix;


      end;


    { TScope }


    constructor TScope.Create;


    begin


      FIx := 8;


    end;


    procedure TScope.Test;


    var


      ix: Integer;


    begin


      for ix  := 1 to 5


       do write(ix, ‘,’, self.ix, ‘  ‘);


    end;


    var


      Scope: TScope;


    begin


      Scope := TScope.Create;


      try


          Scope.Test;


      finally


        Scope.Free;


        Write(‘ – Press Enter’);


        Readln;


      end;


    end.


  4. >The inline var declaration would be a usable alternative, but opens up some issues with f.x. try finally/except – should the variables be accessible in finally/except?


    To the code, no, if you want them to be declare them in a higher level scope. To the debugger, possibly.


    >As for the assignment – and stepping – if you step into, you’d be stepping into both assignments


    Stepping, yes, though it would be confusing, but breakpointing? no.


    And similarly, crash log callstacks would be ambiguous.


    And since “using” would not bring more than it takes away… 🙂


    > Though I personally would prefer that the for-var-loop variable should only be defined inside the for-loop scope.


    I personally prefer things to be completely unambiguous, which means if a new variable is declared, it should be explicit, which the var keyword achieves, and not implicit.


    The syntax overhead is really minimal, but the obviousness benefits are not 🙂

Leave a Reply