VOOZH about

URL: https://dev.to/crabpascal/generics-in-crabpascal-tlist-and-tdictionary-v2150-generics-no-crabpascal-tlist-e-oca

⇱ Generics in CrabPascal: TList and TDictionary (v2.15.0) | Generics no CrabPascal: TList e TDictionary (v2.15.0) - DEV Community


Bilingual post · Post bilíngue

Jump to: English · Português


English {#english}

Generics in CrabPascal: TList and TDictionary (v2.15.0)

Generic collections are the backbone of modern Delphi — caches, registries, JSON maps. Sprint 7 (v2.15.0) shipped System.Generics.Collections with working TDictionary<K,V> and TList<T>, including the all-important TryGetValue with var parameters.

Dictionary basics

program DictDemo;
uses
 System.SysUtils,
 System.Generics.Collections;

var
 D: TDictionary<string, Integer>;
 V: Integer;
begin
 D := TDictionary<string, Integer>.Create;
 try
 D.Add('apples', 3);
 D.Add('oranges', 5);

 if D.TryGetValue('apples', V) then
 WriteLn('apples = ', V)
 else
 WriteLn('not found');
 finally
 D.Free;
 end;
end.

TryGetValue writes into V only when the key exists — the Delphi pattern you expect in production code.

Why var mattered in Sprint 7

Before v2.15.0, var parameters were incomplete. TryGetValue needs write-back semantics: the callee mutates the caller's variable through a reference. CrabPascal introduced Value::Reference and write-back in procedure dispatch specifically for this API surface.

cargo test --test generics_collections

Parser and type syntax

Instantiated generics in expressions now parse cleanly:

D := TDictionary<string, Integer>.Create;
L := TList<string>.Create;

The semantic layer recognizes Generics.Collections when you uses System.Generics.Collections. Method dispatch routes Add, TryGetValue, and Create through intrinsics before empty VMT lookups fail.

TList quick example

var
 L: TList<string>;
begin
 L := TList<string>.Create;
 try
 L.Add('alpha');
 L.Add('beta');
 WriteLn('Count = ', L.Count);
 finally
 L.Free;
 end;
end;

Full TList feature parity (sorting, binary search) evolves in later releases; Sprint 7 nailed construction, Add, Count, and TryGetValue.

Assignment normalization

A implementation detail worth knowing: assignments like D := TDictionary<...>.Create required normalize_call_name in execute_assignment, not only in direct calls. Without that fix, generics looked fine in check but failed mysteriously at runtime.

Real-world use: HTTP handlers

Horse handlers often build response maps:

// simplified pattern
var
 Cache: TDictionary<string, string>;
begin
 Cache := TDictionary<string, string>.Create;
 try
 Cache.Add('Content-Type', 'application/json');
 // ...
 finally
 Cache.Free;
 end;
end;

What's still evolving

Explicit out parameters in the parser, advanced collection algorithms, and full build-exe codegen for generic dispatch were tracked as debt. For learning and API prototyping, v2.15.0 is the sprint where CrabPascal stops telling you to use TStringList as a fake hash map.

Run check, then run, then parity tests when shipping binaries. Generics are the bridge between Pascal nostalgia and code you would write in Delphi 12 today.


Português {#portugus}

Generics no CrabPascal: TList e TDictionary (v2.15.0)

Coleções genéricas são a espinha dorsal do Delphi moderno — caches, registros, mapas JSON. A Sprint 7 (v2.15.0) entregou System.Generics.Collections com TDictionary<K,V> e TList<T> funcionais, incluindo o indispensável TryGetValue com parâmetros var.

Básico de dictionary

program DictDemo;
uses
 System.SysUtils,
 System.Generics.Collections;

var
 D: TDictionary<string, Integer>;
 V: Integer;
begin
 D := TDictionary<string, Integer>.Create;
 try
 D.Add('apples', 3);
 D.Add('oranges', 5);

 if D.TryGetValue('apples', V) then
 WriteLn('apples = ', V)
 else
 WriteLn('not found');
 finally
 D.Free;
 end;
end.

TryGetValue escreve em V só quando a chave existe — o padrão Delphi que você espera em produção.

Por que var importou na Sprint 7

Antes da v2.15.0, parâmetros var estavam incompletos. TryGetValue precisa de write-back: o callee muta a variável do caller via referência. O CrabPascal introduziu Value::Reference e write-back no dispatch de procedures especificamente para essa superfície de API.

cargo test --test generics_collections

Parser e sintaxe de tipos

Genéricos instanciados em expressões agora parseiam limpo:

D := TDictionary<string, Integer>.Create;
L := TList<string>.Create;

A camada semântica reconhece Generics.Collections ao fazer uses System.Generics.Collections. Dispatch de métodos roteia Add, TryGetValue e Create por intrinsics antes de lookups VMT vazios falharem.

Exemplo rápido com TList

var
 L: TList<string>;
begin
 L := TList<string>.Create;
 try
 L.Add('alpha');
 L.Add('beta');
 WriteLn('Count = ', L.Count);
 finally
 L.Free;
 end;
end;

Paridade completa de TList (sort, busca binária) evolui em releases posteriores; a Sprint 7 acertou construção, Add, Count e TryGetValue.

Normalização em assignment

Detalhe de implementação: assignments como D := TDictionary<...>.Create exigiram normalize_call_name em execute_assignment, não só em chamadas diretas. Sem esse fix, genéricos passavam no check mas falhavam misteriosamente em runtime.

Uso real: handlers HTTP

Handlers Horse frequentemente montam mapas de resposta:

// padrão simplificado
var
 Cache: TDictionary<string, string>;
begin
 Cache := TDictionary<string, string>.Create;
 try
 Cache.Add('Content-Type', 'application/json');
 // ...
 finally
 Cache.Free;
 end;
end;

O que ainda evolui

Parâmetros out explícitos no parser, algoritmos avançados de coleção e codegen build-exe completo para dispatch genérico ficaram como débito. Para aprendizado e prototipagem de API, a v2.15.0 é a sprint em que o CrabPascal para de pedir TStringList como hash map falso.

Rode check, depois run, depois testes de paridade ao entregar binários. Generics são a ponte entre nostalgia Pascal e código que você escreveria no Delphi 12 hoje.


Published on dev.to/@crabpascal · Código em CrabPascal