TScope

Nested structures increase complexity in two ways. First, they deepen the shape of the code itself, making methods longer, more indented, and harder to scan. Second, they increase cognitive load by forcing the reader to track more ownership rules, cleanup paths, and execution branches.

Every additional try/finally, try/except, or conditional cleanup path contributes to complexity. The reader has to keep more state in mind: which objects are owned here, which ones escape, which ones are freed on failure, and which ones survive success.

One of my favourite types in Project Galahad is TScope. Its purpose is to reduce the need for try/finally and try/catch structures. Its concise semantics also simplifies assignment.

It combines the C# using declaration with the defer found in Go.

As a managed record, when it goes out of scope it frees any objects it owns. Optionally, it can execute clean up code before it frees objects. In a nutshell:

var scope: TScope;
begin
  // Automatically freed when the procedure ends
  var List := scope.Owns(TStringList.Create); 
  var Map  := scope.Owns(TDictionary.Create);
  
  // Optional Go-style defer
  scope.Defer(procedure begin Writeln('Run when exiting scope...'); end);
  
  List.Add('Galahad makes this easy.');
  
  scope.Defer(procedure begin Writeln('Run after the previous function...'); end);

  // if there is an exception, List is cleaned up, otherwise we want to return it.
  Result := scope.Release(List);
end;
TScope does not remove the need to think about ownership. It makes ownership easier to express:
  • objects created here are owned here
  • objects that must escape are explicitly released
  • deferred cleanup stays close to the logic that needs it

This leads to shorter methods, less nesting, and code that is easier to reason about.

This is an example of where Delphi lacks syntax, but offers the power to implement a solution.