Sunday, August 31, 2008

Anonymous methods in testing / profiling - Pt. 2

In the last post, I only went into profiling. Here's a simple testing usage scenario. Suppose you want to make sure that a certain body of code throws an exception of a particular type. That means you have to wrap it up in a try / except block, and test the type of the exception. The logic to do this would be quite repetitive if it had to be expanded out long-hand. Fortunately, that's not the case:

procedure ExpectException(ExClass: ExceptClass; const Code: TProc);
var
  noThrow: Boolean;
begin
  noThrow := False;
  try
    Code;
    noThrow := True;
  except
    on e: Exception do
      if not e.ClassType.InheritsFrom(ExClass) then
        raise Exception.CreateFmt('Failed - %s expected, %s actually raised',
          [ExClass.ClassName, e.ClassName]);
  end;
  if noThrow then
    raise Exception.CreateFmt('Failed - %s not raised', 
      [ExClass.ClassName]);
end;

Here it is in use:

var
  list: TList;
begin
  list := TList.Create;
  try
    
    ExpectException(EListError, procedure
    begin
      list[42] := nil;
    end);
    
  finally
    list.Free;
  end;
end;

Anonymous methods in testing / profiling situations

One situation in which anonymous methods are particularly useful is for ad-hoc profiling and testing scenarios. For both profiling and testing, it is code itself that one wants to work with. I.e. the question you are trying to answer is "test this code", or "time this code". Using code as a parameter is a natural fit for this problem.

I'll start with a very simple profile harness. It does little more than run a code block a number of times and report on how long it took. Here's the definition of the class:

  TBenchmarker = class
  private
    const
      DefaultIterations = 3;
      DefaultWarmups = 1;
    var
      FReportSink: TProc<string,Double>;
      FWarmups: Integer;
      FIterations: Integer;
      FOverhead: Double;
    class var
      FFreq: Int64;
    class procedure InitFreq;
  public
    constructor Create(const AReportSink: TProc<string,Double>);

    class function Benchmark(const Code: TProc; 
      Iterations: Integer = DefaultIterations; 
      Warmups: Integer = DefaultWarmups): Double; overload;

    procedure Benchmark(const Name: string; const Code: TProc); overload;
    function Benchmark<T>(const Name: string; const Code: TFunc<T>): T; overload;

    property Warmups: Integer read FWarmups write FWarmups;
    property Iterations: Integer read FIterations write FIterations;
  end;

This API has a couple of interesting features. The constructor takes method reference argument indicating where output - the results of the benchmarking - will go. This idiom makes it easy for users of the class to direct the output. It's loosely coupled, just like an event (after all, an event - a method pointer - is almost the same thing as a method reference), but it also doesn't require that the user create a whole method just for the purpose of sending the output off to whereever it ends up (on the console, in a UI, etc.).

Secondly, the Benchmark overloaded methods themselves - these take the actual code to be measured. The Benchmark overloads will execute the passed-in code a number of times for warmup, and then a number of times for the actual measurement. The return value of the class function version is the number of seconds an average iteration took; the instance function versions send off the result to the reporting sink.

These two idioms - data sinks and sources, and taking in a block of code which can be executed a carefully constructed context - are common usage patterns of anonymous methods. I'll get into them further in later posts.

Here's the Benchmark class in practice:

procedure UseIt;
const
  CallDepth = 10000;
var
  b: TBenchmarker;
  x: TSomeClass;
  intf: ISomeInterface;
begin
  b := TBenchmarker.Create(procedure(Name: string; Time: Double)
  begin
    Writeln(Format('%-20s took %15.9f ms', [Name, Time * 1000]));
  end);
  try
    b.Warmups := 100;
    b.Iterations := 100;

    x := TSomeClass.Create;
    intf := x;
      
    b.Benchmark('Static call', procedure
    begin
      x.StaticCall(x, CallDepth);
    end);

    b.Benchmark('Virtual call', procedure
    begin
      x.VirtCall(x, CallDepth);
    end);

    b.Benchmark('Interface call', procedure
    begin
      intf.IntfCall(intf, CallDepth);
    end);
    
  finally
    b.Free;
  end;
end;

The benefits of variable capture should be clear here. The segments of code to be benchmarked can freely access variables in the outer scope.

Finally, here's the complete implementation, including TBenchmark definition and use:

program am_profiler;

{$APPTYPE CONSOLE}

uses
  Windows, SysUtils;

type
  TBenchmarker = class
  private
    const
      DefaultIterations = 3;
      DefaultWarmups = 1;
    var
      FReportSink: TProc<string,Double>;
      FWarmups: Integer;
      FIterations: Integer;
      FOverhead: Double;
    class var
      FFreq: Int64;
    class procedure InitFreq;
  public
    constructor Create(const AReportSink: TProc<string,Double>);
    class function Benchmark(const Code: TProc; 
      Iterations: Integer = DefaultIterations; 
      Warmups: Integer = DefaultWarmups): Double; overload;
    procedure Benchmark(const Name: string; const Code: TProc); overload;
    function Benchmark<T>(const Name: string; const Code: TFunc<T>): T; overload;
    property Warmups: Integer read FWarmups write FWarmups;
    property Iterations: Integer read FIterations write FIterations;
  end;
  
{ TBenchmarker }

constructor TBenchmarker.Create(const AReportSink: TProc<string, Double>);
begin
  InitFreq;
  FReportSink := AReportSink;
  FWarmups := DefaultWarmups;
  FIterations := DefaultIterations;
  
  // Estimate overhead of harness
  FOverhead := Benchmark(procedure begin end, 100, 3);
end;

class procedure TBenchmarker.InitFreq;
begin
  if (FFreq = 0) and not QueryPerformanceFrequency(FFreq) then
    raise Exception.Create('No high-performance counter available.');
end;

procedure TBenchmarker.Benchmark(const Name: string; const Code: TProc);
begin
  FReportSink(Name, Benchmark(Code, Iterations, Warmups) - FOverhead);
end;

class function TBenchmarker.Benchmark(const Code: TProc; Iterations,
  Warmups: Integer): Double;
var
  start, stop: Int64;
  i: Integer;
begin
  InitFreq;
  
  for i := 1 to Warmups do
    Code;

  QueryPerformanceCounter(start);
  for i := 1 to Iterations do
    Code;
  QueryPerformanceCounter(stop);
  
  Result := (stop - start) / FFreq / Iterations;
end;

function TBenchmarker.Benchmark<T>(const Name: string; const Code: TFunc<T>): T;
var
  start, stop: Int64;
  i: Integer;
begin
  for i := 1 to FWarmups do
    Result := Code;
  
  QueryPerformanceCounter(start);
  for i := 1 to FIterations do
    Result := Code;
  QueryPerformanceCounter(stop);
  
  FReportSink(Name, (stop - start) / FFreq / Iterations - FOverhead);
end;

type
  ISomeInterface = interface
    procedure IntfCall(const Intf: ISomeInterface; depth: Integer);
  end;
  
  TSomeClass = class(TInterfacedObject, ISomeInterface)
  public
    procedure VirtCall(Inst: TSomeClass; depth: Integer); virtual;
    procedure StaticCall(Inst: TSomeClass; depth: Integer);
    procedure IntfCall(const Intf: ISomeInterface; depth: Integer);
  end;

{ TSomeClass }

procedure TSomeClass.IntfCall(const Intf: ISomeInterface; depth: Integer);
begin
  if depth > 0 then
    Intf.IntfCall(Intf, depth - 1);
end;

procedure TSomeClass.StaticCall(Inst: TSomeClass; depth: Integer);
begin
  if depth > 0 then
    StaticCall(Inst, depth - 1);
end;

procedure TSomeClass.VirtCall(Inst: TSomeClass; depth: Integer);
begin
  if depth > 0 then
    VirtCall(Inst, depth - 1);
end;

procedure UseIt;
const
  CallDepth = 10000;
var
  b: TBenchmarker;
  x: TSomeClass;
  intf: ISomeInterface;
begin
  b := TBenchmarker.Create(procedure(Name: string; Time: Double)
  begin
    Writeln(Format('%-20s took %15.9f ms', [Name, Time * 1000]));
  end);
  try
    b.Warmups := 100;
    b.Iterations := 100;

    x := TSomeClass.Create;
    intf := x;
      
    b.Benchmark('Static call', procedure
    begin
      x.StaticCall(x, CallDepth);
    end);

    b.Benchmark('Virtual call', procedure
    begin
      x.VirtCall(x, CallDepth);
    end);

    b.Benchmark('Interface call', procedure
    begin
      intf.IntfCall(intf, CallDepth);
    end);
    
  finally
    b.Free;
  end;
end;

begin
  try
    UseIt;
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.

Friday, August 22, 2008

Venting on Vista

First up - apologies! This post has no technical content whatsoever about compilers, programming languages or Delphi. It's just a rant: I want to document my Vista annoyances so that I can hopefully move on. You know, denial, acceptance, and all that. Maybe I'll even look back on this post a few years from now and see where I was wrong!

Shell

Almost all my annoyances about Vista come from the Explorer shell. I've reached the point where I am somewhat seriously considering writing my own shell that supports the bare minimum, but the fact that file dialogs would be beyond my reach without some fairly hardcore system DLL replacement holds me back. For now.

Non-configurable Explorer Toolbar

The toolbar is no longer configurable, and in particular, it is no longer possible to move all three components - menu, tool buttons and location - onto a single row to maximize client area. Here's what I mean: my default Explorer setup has a height, from top of window to start of listview client area, of 65 pixels:

The equivalent in Vista has a height of 125 pixels, i.e. wasting over 90% more screen space for no benefit:

And yes, I run Vista without any themes, for a number of reasons: I'm not impressed, the compositing uses resources I'd rather spend on other things, and I often interact with Vista using RDP and I'd rather not have to deal with two different UI styles. And yes, I want that menu bar: it has useful commands like, you know, map network drive.

Missing XP-style Start Menu

This is the next biggest issue. If they had put in an option to have an XP-style menu combined with the new search box, a good deal of grief would have been forgiven. As it is, I detest the new start menu. The only backward-compatible option is a Windows 95 option, which is bizarre from my point of view - XP has been Microsoft's longest selling OS, and it's the immediate predecessor to their new version, and the backward compatible option they offer is for an obsolete style that's over 10 years old? It makes no sense to me.

Here are some of the niggles with the new menu that annoy me so much.

Constant clicking

With XP, the fact that a menu expanded when hovered over meant that muscle memory could be used for navigation. I've used TweakUI to reduce this hover time so that it's just long enough that mouse navigation doesn't cause redundant sub-menu opening. On Vista, I have to click and scroll around to find things. I'm almost in the same situation as if I had to browse through the shortcuts in the file system directly with Explorer.

Constrained tiny little box

With the XP menu, I have the entire height of the screen in which to show a submenu. Since I keep my top-level submenus strictly categorized, with headers like Development, Tools, CodeGear etc. for direct access to application shortcuts, and a catch-all group called Groups which contains all those start-menu folders that application vendors add, I have very dense menus with an efficient navigation experience, a lot like the Control Panel menu (more on that next). Not so on Vista. Everything is crammed into a tiny little box, complete with wee little scrollbars, even while I have a relatively vast expanse of 24" monitor begging to be used.

Missing GUI Navigation in Control Panel

I mostly use the Control Panel menu from the Start Menu to access control panel applets, because (as a developer) I'm always tweaking this or that setting.

Unfortunately, Microsoft have added more hierarchy to the control panel (not a problem, in my view, because there are more options in Vista), but have provided less means of navigating it! Here are some concrete examples.

No submenus in Control Panel menu from Start Menu

Look at the XP Control Panel menu:

Now take a look at the Vista Control Panel menu:

Spot the differences? The XP menu provides direct access to printers, network adapters and scheduled tasks, etc.; whereas the Vista menu has hidden those things away inside the applets themselves.

Now, the Vista equivalent of Scheduled Tasks is a lot more capable, but it's also a lot more complicated, seems to expose some innards of the Windows system such that it's easier to break, and has a dog-slow UI with three - I counted them, three - nested client areas in the center panel. I'm not convinced that the old shell view extension mechanism wasn't better.

Inconsistent Explorer / Control Panel navigation

It's a similar story for network adapters. I occasionally have need to change IP or other settings for various reasons. For example, all my desktop machines have multiple ethernet adapters and I ocassionally route wireless traffic from a base station in one port and out the other to the broadband router.

Since the Control Panel submenu is no longer available, I have to go through the new "Network and Sharing Center" and try to figure out where the adapter configuration is hidden. Eventually I alight upon "Manage network connections", but this pops up a separate window which appears to be a subfolder of the Control Panel called "Network Connections" - at least, judging by the location bar:

However, try to find this folder either by navigating the control panel or by browsing the control panel's shell namespace tree, and you won't find it. The quickest way to get back to it is by creating a shortcut, but that's not so easy to figure out either, seeing how Microsoft removed the "Favorites" menu. What you need to do is click and drag the small icon in the location bar into some Explorer folder view, like the desktop.

And that brings me to another point. In Vista, you can't explore the Control Panel namespace using Explorer's treeview. In fact, if you start out in an Explorer window that has a treeview visible, and you select the Control Panel child of the Desktop namespace, the treeview disappears. I find this disturbing and disconcerting. It's almost like the feature had bugs, so they had to disable that way of accessing the Control Panel. Even the new context-sensitive Explorer toolbar disappears when the Control Panel is entered, removing with it the ever useful "rotate icon size" (Views) button that looks like a menu, and the Organize menu that looks like a button.

Missing Security tab for multiple selection

Onwards, to more practical every-day matters. I have multiple hard drives (11 3.5" disks at last count, totalling 4.75 ISO-TB, or about 4.3 real TB), installed across three desktop machines. These drives are almost all formatted with NTFS, with a single ext3 disk for Linux. Apart from OS boot drives, most of these drives have no fixed abode; they are liable to be swapped around or accessed from laptops after being installed in enclosures (I have two USB enclosures that can take any one of SATA, 3.5" IDE and 2.5" IDE drives).

If you've done this yourself, you'll know the problem that crops up (and it is somewhat exacerbated by Cygwin): the permissions for files and directories on the disks often embed machine-specific SIDs, meaning that you have to reset permissions to get access. This isn't a totally trivial matter: sometimes, you need one recursive pass to take ownership, and a second pass to replace permissions, if the original permissions were restrictive.

So, resetting permission on a handful of folders using Explorer should be a piece of cake, right? Here's the dialog for three selected folders in XP:

And here it is in Vista:

Or rather, here it isn't. I see No Good Reason for this.

Missing menus

It has become fashionable for applications designed for Vista to hide menu bars. Unfortunately, they usually haven't been replaced by a viable alternative. For example, examine the Windows Media Player for Vista:

Suppose you have copied the URL of a movie stream from the Internet and want to watch it in full screen etc. in a proper media player, rather than in a tiny plugin window - which by itself often slows down and destabilizes the browser. Now, I know that previous versions of WMP have an 'Open URL' menu item. I know that the Open command is on the File menu, and has been since the days of Windows 3.0 and before.

The only question is - where the heck is the File menu? I'm being deadly serious: I have been in this exact scenario, and I had no idea where to go. It turns out that the menu has been hidden as a context menu on some of the gaps in the toolbars. This is only one of the reasons I use Media Player Classic for almost all video playback.

You know, it's this kind of behaviour - prioritizing asthetics above functionality - that makes me hate Apple. At least Apple are usually more consistent and slightly more usable, if you're willing to accept the concomitant lack of functionality and user freedom, and general vibe of submission to the will of a tyrant. A tyrant (Jobs) with good taste is still a tyrant, and ought be shunned, if you value long-run freedom.

Missing keyboard accelerator hints

And on the topic of menus... where are the keyboard accelerator hints? You know, those little underlines that show which key, in combination with the Alt key, will select that menu item. They were disabled in XP by default too, but were easily re-enabled in Display Properties | Appearance | Effects. The analogous location in Vista has far fewer options.

Inconsistently extended right-click area in detailed listviews

Onwards to the rest of the main Explorer window itself. This bug - I can only call it a bug - catches me every time I work with a large folder in Vista. Observe, a folder with lots of files, and one selected; a common starting point:

Note the full selection width, which is different from XP, where only the icon and its associated text were marked selected.

Suppose I want to create a new folder. Where should I right-click? In XP, anywhere outside the selection and not directly on an icon or its associated text would do. Let's try that:

Ha ha! You thought wrong, buddy!

If you've played around with the new selection rules in Vista, you'll know that any text associated with a row in a detailed listview counts as item area. Now, I don't like this new behaviour: I use detailed view all the time, and because I like dense information, there is relatively little free space for folder context operations - most of the columns are filled with text.

It gets worse, though. How do you select multiple items using a drag rectangle in this new paradigm? To do this, you must deliberately leave wasted space to the right of all columns, so that you can begin your drag rectangle there. If you don't, the initial drag operation is interpreted as initiating a file move operation.

But wait! What about the weird behaviour seen above, where the item context menu pops up even though I'm not clicking on any text? This is a listview bug, and still hasn't been fixed even though Vista has been out for ages now. Let me demonstrate. This is the logical item area, highlighted as best I can in yellow:

And this is the actual item area:

The selection logic doesn't take account of the column alignment. This causes me amazing amounts of frustration, because the biggest chunk of free space for right-click operations for me is between the left-aligned Name column and the right-aligned Size column. There's usually plenty of space here to account for huge files and long names; but there's an invisible item-associated click area right down the middle of it.

Treeview draw gaps

This one is minor, but still annoying. With effects turned off, treeviews that don't have either the focus or the cursor inside their bounds don't draw plus/minus boxes for folders that have children. It just looks plain ugly:

Listview deletes kill focus

Suppose you're reviewing a list of files, and deleting some of them as you scroll through the list. I often do this with the keyboard, as it's more efficient - the arrows navigate nicely and the delete button is nearby (I have confirmation dialogs disabled for deletion). Unfortunately, if you happen to delete multiple items in one go, you're in for a surprise.

To be more specific, if you select a range of items by holding the shift key down while scrolling through a long list, like this:

And then delete them, you'll notice that the focus rectangle has disappeared:

Where did it go? I sure hope I didn't lose my place in that long list of files I was reviewing! Let's see, I press the Up arrow to see if that teases out the focus from hiding, but no luck. I press the Down arrow, and hey presto!

I'm right back at the start. Now I need to scroll through all those files again and pick up where I left off. This does not make me happy.

File system watching maintains sorted order

I often keep a folder open while a background process is creating files in it: anything from an automated build producing log files to a wget mass download job. Now, maybe I'm a really odd fellow, but I rather appreciate the way XP behaves in this scenario: periodically, the kernel notifies Explorer that the underlying directory has changed, and XP will add the new items at the end of the existing listing - even though this may be out of the correct sorting order for the folder. I know that I can just press 'F5' to refresh the view, and everything will be resorted. However, knowing that new files are just appended to the end, I can scroll down there and watch the output appear, and even open it up when I see that the next output file has been created.

In Vista, no longer. Explorer is now "smarter", for some smart-ass value of smarter. Instead, it incorporates new items into the existing sorting order. Now I know some people would consider the old behaviour a bug and the new a fix, but the old behaviour was long-standing, and I had learned to rely on it.

Conclusion

Considering some of the lower-level enhancements in Vista, none of the above items individually would (or should) have been enough to turn me against it. However, when you add it all up, the charge starts getting more serious. I can't use Vista for more than 10 minutes without hitting one or more of these snags (or others that I haven't gone into); and these are just the snags I can't really do anything about.

Other things, like the indexing service, overhead of desktop composition etc., I have all disabled as either inessential or things I already have solutions for.

What I would have liked most is the Vista kernel with an optional selection of Vista applets, combined with an option for selecting the XP shell. In other words, I would have been far happier with a whole lot less. It's not like value for money comes into it: if you have to buy a Microsoft OS to put on a machine you build, the purchase feels more like a tax than it does choice. It was never a matter of a new OS driving me to purchase it, or a new PC.

The product feels oddly like a guilty company's justification for selling a product at a high price but a low marginal cost. It reminds me of government spending; they collect all this money, then have to find ways to spend it, but because of their monopoly position, they don't have the correct incentives, and consequently much of that money would have been better left with its original owner.

Wednesday, August 20, 2008

Tiburon: fun with generics and anonymous methods

Update: I've added more code to the end of this entry.

The next release, Delphi 2009 (codename Tiburón) has some new language features: generics and anonymous methods in particular. I've decided to write up some of my experiments, playing with the new tools to see what can be done with them.

First, though, a disclaimer: I am of course working with the development tip for both compiler and RTL, so some details mentioned here (RTL in particular) may change before final release. However, the basics of generics and anonymous methods are pretty solid and should not change, so any gaps should be fillable if needs be.

Here's a generic method reference:

type
  TFunc<T> = reference to function: T;

This guy (a "method reference") can refer to any global function, method or anonymous method which takes no parameters and returns a value. It's a managed type, like strings and interfaces, because it can contain state that needs to be freed when the last reference to it goes out of scope. Here's a trivial example of a method reference in use with an anonymous method:

{$APPTYPE CONSOLE}

uses
  SysUtils;

function MakeCounter(Start, Increment: Integer): TFunc<Integer>;
begin
  Result := function: Integer
  begin
    Result := Start;
    Inc(Start, Increment);
  end;
end;

procedure WriteList(const Source: TFunc<Integer>; Count: Integer);
begin
  while Count > 1 do
  begin
    Write(Source, ', ');
    Dec(Count);
  end;
  Writeln(Source);
end;

procedure DoIt;
var
  evenNumbers: TFunc<Integer>;
  decades: TFunc<Integer>;
begin
  evenNumbers := MakeCounter(0, 2);
  decades := MakeCounter(0, 10);

  WriteList(evenNumbers, 10);
  WriteList(decades, 10);
end;

begin
  try
    DoIt;
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.

The code should be pretty easy to follow. It prints out the first 10 odd numbers, and the first 10 numbers evenly divisible by 10, starting at 0. Here's the exact output:

0, 2, 4, 6, 8, 10, 12, 14, 16, 18
0, 10, 20, 30, 40, 50, 60, 70, 80, 90

Notice how the value returned from MakeCounter has captured its parameters, and that these parameters have become internal state of the return value. This action is called variable capture, and an anonymous method that performs variable capture is a closure. TFunc<T> is useful enough that it is predefined in SysUtils; there are similar versions taking up to 4 arguments, and there are also TProc method references, also taking up to 4 arguments.

This example suggests to me an easy way of creating enumerators in Delphi. Rather than having to define a separate enumerator class, and implement the GetCurrent and MoveNext functions, as well as a property that reads from GetCurrent, we can use an anonymous method that simply returns each value in sequence. To signal termination, we'll use a special exception. With these tools, we can write a counter generator that has an upper bound too:

{$APPTYPE CONSOLE}

uses
  SysUtils;

type
  EEnumerationFinished = class(Exception)
    constructor Create;
  end;
  
  TSimpleEnumerator<T> = class
  private
    FSource: TFunc<T>;
    FCurrent: T;
    FFinished: Boolean;
    function GetCurrent: T;
  public
    constructor Create(const ASource: TFunc<T>);
    function MoveNext: Boolean;
    property Current: T read GetCurrent;
  end;

{ EEnumerationFinished }

constructor EEnumerationFinished.Create;
begin
  inherited Create('Enumeration finished');
end;
  
{ TSimpleEnumerator<T> }

constructor TSimpleEnumerator<T>.Create(const ASource: TFunc<T>);
begin
  FSource := ASource;
end;

function TSimpleEnumerator<T>.GetCurrent: T;
begin
  Result := FCurrent;
end;

function TSimpleEnumerator<T>.MoveNext: Boolean;
begin
  if FFinished then
    Exit(False);
  try
    FCurrent := FSource;
  except
    on e: EEnumerationFinished do
    begin
      FFinished := True;
      Exit(False);
    end;
  end;
  Exit(True);
end;

function MakeCounter(Start, Increment, Count: Integer): TSimpleEnumerator<Integer>;
begin
  Result := TSimpleEnumerator<Integer>.Create(function: Integer
  begin
    if Count <= 0 then
      raise EEnumerationFinished.Create;
    Result := Start;
    Inc(Start, Increment);
    Dec(Count);
  end);
end;

function ToString(e: TSimpleEnumerator<Integer>): string;
var
  sb: TStringBuilder;
begin
  sb := TStringBuilder.Create;
  try
    while e.MoveNext do
      sb.Append(e.Current).Append(', ');
    if sb.Length > 2 then
      sb.Length := sb.Length - 2;
    Result := sb.ToString;
  finally
    sb.Free;
  end;
end;

procedure DoIt;
var
  oddNums: TSimpleEnumerator<Integer>;
begin
  oddNums := MakeCounter(1, 2, 20);
  try
    Writeln('Odd numbers: ', ToString(oddNums));
    Writeln('And again: ', ToString(oddNums));
  finally
    oddNums.Free;
  end;
end;

begin
  try
    DoIt;
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.

Unfortunately, this has several drawbacks. First, the for-in syntax can't be used, because that expects a collection of some kind that has a method called GetEnumerator, whereas we only have the enumerator itself. Secondly, the enumeration can only be performed once. You can see this in the output:

Odd numbers: 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39
And again: 

We can fix both of these problems, however, by adding another layer of indirection. Rather than working with TFunc<T>, we'll work with TFunc<TFunc<T>>; and we'll write a TSimpleEnumerable<T> that returns our enumerator.

Now, inside one of the new units, Generics.Collections, are a couple of new base classes, TEnumerator<T> and TEnumerable<T>. I'll use those base classes too:

{$APPTYPE CONSOLE}

uses
  SysUtils, Generics.Collections;

type
  EEnumerationFinished = class(Exception)
    constructor Create;
  end;

  TSimpleEnumerable<T> = class(TEnumerable<T>)
  private
    FSourceMaker: TFunc<TFunc<T>>;
  public
    type
      TEnumerator = class(TEnumerator<T>)
      private
        FSource: TFunc<T>;
        FCurrent: T;
        FFinished: Boolean;
        constructor Create(const ASource: TFunc<T>);
        function GetCurrent: T;
      protected
        function DoGetCurrent: T; override;
        function DoMoveNext: Boolean; override;
      public
        function MoveNext: Boolean;
        property Current: T read GetCurrent;
      end;
  protected
    function DoGetEnumerator: TEnumerator<T>; override;
  public
    constructor Create(const ASourceMaker: TFunc<TFunc<T>>);
    function GetEnumerator: TEnumerator;
  end;
  
{ EEnumerationFinished }

constructor EEnumerationFinished.Create;
begin
  inherited Create('Enumeration finished');
end;
  
{ TSimpleEnumerable<T>.TEnumerator }

constructor TSimpleEnumerable<T>.TEnumerator.Create(const ASource: TFunc<T>);
begin
  FSource := ASource;
end;

function TSimpleEnumerable<T>.TEnumerator.DoGetCurrent: T;
begin
  Result := GetCurrent;
end;

function TSimpleEnumerable<T>.TEnumerator.DoMoveNext: Boolean;
begin
  Result := MoveNext;
end;

function TSimpleEnumerable<T>.TEnumerator.GetCurrent: T;
begin
  Result := FCurrent;
end;

function TSimpleEnumerable<T>.TEnumerator.MoveNext: Boolean;
begin
  if FFinished then
    Exit(False);
  try
    FCurrent := FSource;
  except
    on e: EEnumerationFinished do
    begin
      FFinished := True;
      FCurrent := Default(T); // don't keep alive if refcounted
      Exit(False);
    end;
  end;
  Result := True;
end;

{ TSimpleEnumerable<T> }

constructor TSimpleEnumerable<T>.Create(const ASourceMaker: TFunc<TFunc<T>>);
begin
  FSourceMaker := ASourceMaker;
end;

function TSimpleEnumerable<T>.DoGetEnumerator: TEnumerator<T>;
begin
  Result := GetEnumerator;
end;

function TSimpleEnumerable<T>.GetEnumerator: TEnumerator;
begin
  Result := TEnumerator.Create(FSourceMaker());
end;

function MakeCounter(Start, Increment, Count: Integer): TSimpleEnumerable<Integer>;
begin
  Result := TSimpleEnumerable<Integer>.Create(function: TFunc<Integer>
  var
    myStart, myIncrement, myCount: Integer;
  begin
    myStart := Start;
    myIncrement := Increment;
    myCount := Count;
    
    Result := function: Integer
    begin
      if myCount <= 0 then
        raise EEnumerationFinished.Create;
      Result := myStart;
      Inc(myStart, myIncrement);
      Dec(myCount);
    end;
  end);
end;

function ToString(Collection: TEnumerable<Integer>): string;
var
  sb: TStringBuilder;
  item: Integer;
begin
  sb := TStringBuilder.Create;
  try
    for item in Collection do
      sb.Append(item).Append(', ');
    if sb.Length > 2 then
      sb.Length := sb.Length - 2;
    Result := sb.ToString;
  finally
    sb.Free;
  end;
end;

procedure DoIt;
var
  oddNums: TEnumerable<Integer>;
begin
  oddNums := MakeCounter(1, 2, 20);
  try
    Writeln('Odd numbers: ', ToString(oddNums));
    Writeln('And again: ', ToString(oddNums));
  finally
    oddNums.Free;
  end;
end;

begin
  try
    DoIt;
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.

This did require a little extra complexity - making a copy of the state in the outer anonymous method of MakeCounter - but it also now performs as expected:

Odd numbers: 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39
And again: 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39

So there we have it: an easy way to create enumerators and simple collection wrappers using a combination of generics and anonymous methods. There's more that can be built on this foundation, but I'll leave that until later.

Update:

Joylon in the comments prompts me to show the alternative - i.e. how to write the above without using anonymous methods. Here's a basic enumerator which does the same thing as the above - i.e. generate an integer sequence of a specified length and stride:

type
  TEnumerator = class
  private
    FCurrent: Integer;
    FCount: Integer;
    FIncrement: Integer;
    function GetCurrent: Integer;
  public
    constructor Create(Start, Increment, Count: Integer);
    function MoveNext: Boolean;
    property Current: Integer read GetCurrent;
  end;
  
{ TEnumerator }

constructor TEnumerator.Create(Start, Increment, Count: Integer);
begin
  FCurrent := Start;
  FCount := Count;
  FIncrement := Increment;
end;

function TEnumerator.GetCurrent: Integer;
begin
  Result := FCurrent;
end;

function TEnumerator.MoveNext: Boolean;
begin
  if FCount <= 0 then
    Exit(False);
  Inc(FCurrent, FIncrement);
  Dec(FCount);
  Result := True;
end;

At 35 lines, it is quite a bit (3x) more verbose than the directly equivalent MakeCounter function (11 lines; this isn't one that supports resetting or recreating the enumerator, but the translation above doesn't either):

function MakeCounter(Start, Increment, Count: Integer): TSimpleEnumerator<Integer>;
begin
  Result := TSimpleEnumerator<Integer>.Create(function: Integer
  begin
    if Count <= 0 then
      raise EEnumerationFinished.Create;
    Result := Start;
    Inc(Start, Increment);
    Dec(Count);
  end);
end;

However, even then, I wouldn't necessarily recommend using the above technique to implement all enumerators in general - the fact that exceptions are used to signal termination will cause some performance loss and, more importantly, quite a bit of annoyance when running in a debugger.

Rather, I intend, over the next few posts in this blog, to use anonymous methods in all sorts of ways so that you, the reader, might see opportunities for their creative use in your own code.