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
Top comments (0)