Pascal Framework is a foundational library for Free Pascal and Lazarus applications, providing enterprise-grade components and utilities that extend the standard FCL and LCL libraries. The framework addresses the gap between Pascal's standard libraries and the demands of modern application development by offering production-ready implementations of common patterns, data structures, and UI components.
The library is designed for developers building desktop applications with Lazarus/FPC who need robust, reusable solutions for collection manipulation, caching, memory management, data binding, and UI components. Rather than reinventing these patterns in each project, Pascal Framework provides battle-tested implementations that handle edge cases, threading concerns, and memory management correctly.
Pascal Framework is built on several core principles:
Correctness Over Convenience: Components prioritize correct behavior, particularly around memory management, threading safety, and edge cases. The framework provides explicit dispose policies and memory scope management rather than relying on implicit behavior that can lead to leaks or undefined behavior.
Composability: The library emphasizes small, composable abstractions. Comparers, predicates, and transformers can be combined through tool classes like TComparerTool and TPredicateTool, allowing complex behavior to be built from simple functions.
Extensibility Through Interfaces: Core abstractions are defined as interfaces (IComparer<T>, IPredicate<T>, ITransformer<T>) with multiple implementation paths (nested functions, object methods, global functions) to support different coding styles and use cases.
Explicit Resource Management: The framework provides tools like TDisposables for scope-based resource cleanup, reducing the risk of leaks while maintaining explicitness about object lifetimes.
Cross-Platform Compatibility: All components work with both Free Pascal Compiler (FPC) and include compatibility shims where needed, such as the TTimeSpan implementation for FPC that mirrors Delphi's API.
The UCommon unit provides fundamental utilities that extend Pascal's standard library, including:
- Type Extensions: Helper types and functions for working with hex strings, byte arrays, and variant types
- Temporal Types: A complete
TTimeSpanimplementation for FPC that provides Delphi compatibility, supporting duration arithmetic and formatting - Result Pattern: A
TResulttype that encapsulates success/failure with optional messages and return values, enabling functional error handling - Event System:
TNotifyManyEventandTThreadNotifyfor multi-cast events with thread-safe invocation and main-thread marshalling - Array Utilities: Generic
TArrayTool<T>for common array operations like searching, insertion, removal, and concatenation - DateTime Extensions: Rich helpers for date/time manipulation, comparison, and formatting
- Variant Tools: Safe variant type checking, parsing, and comparison utilities
Advanced collection utilities that extend Generics.Collections:
- Comparer Composition:
TComparerTool<T>for building comparers from functions, inverting comparers, or chaining multiple comparers - Predicate Logic:
TPredicateTool<T>for creating predicates from functions and combining them with logical AND/OR operations - List Filtering:
TListTool<T>for filtering and removing items from lists based on predicates with configurable dispose policies - Transformations:
TListTool<T1, T2>for mapping arrays and lists through transformation functions - Collection Extensions: Utilities for working with specialized collections like
TSortedHashSet<T>
These tools support nested functions, object methods, and global functions, providing flexibility in how predicates and comparers are defined.
A sophisticated caching system with configurable policies:
- Generic Cache Base:
TCacheBase<TKey, TValue>provides the foundation for both action-based and data-driven caches - Expiration Policies: Time-based expiration calculated from fetch time or last access time
- Reap Policies: Multiple eviction strategies including LRU, idle time, size-based (largest/smallest), and oldest-first
- Null Value Handling: Configurable behavior for null values (cache normally, return but don't cache, or throw)
- Action Cache:
TActionCache<TKey, TValue>automatically fetches values on cache miss using provided functions - Size Management: Automatic capacity management with configurable maximum sizes and size estimation
The cache implementation is thread-safe using read-write synchronization and supports bulk loading, invalidation, and flushing.
Explicit memory and object lifetime management:
- Scope-Based Cleanup:
TDisposablesprovides automatic cleanup of objects and memory at scope exit - Multiple Dispose Policies: Support for various cleanup strategies including nil, free, free-and-nil, and FreeMem
- Exception Safety: Cleanup continues even if individual dispose operations throw exceptions
- Flexible Registration: Add objects or memory allocations to disposables at any point during scope execution
This enables RAII-like patterns in Pascal code without language-level support.
Infrastructure for data manipulation and binding:
- Data Rows:
TDataRowprovides a variant-based dynamic record type with named field access - Column Metadata:
TDataColumnfor defining column schemas with key designation - Filter Criteria: Rich filtering support including text matching (exact, prefix, suffix, substring) and numeric comparisons (equality, ranges, inequalities)
- Sort Specifications: Multi-column sorting with ascending/descending direction and null handling policies
- Generic Delegates:
TApplyFilterDelegate<T>andTApplySortDelegate<T>enable custom filtering and sorting logic - Data Tables: In-memory table structures with column definitions and variant-based row storage
Enterprise-grade UI components for Lazarus:
Application Framework (UCommon.UI):
TApplicationFormbase class with lifecycle events (first activation, destruction)TThrottledEventfor rate-limiting event notifications with multiple throttling modes- Helper extensions for
TWinControlandTForm
Wizard Framework (UWizard):
- Generic wizard system
TWizard<T>with model-driven screen flow - Dynamic path updates allowing screens to conditionally inject or replace subsequent screens
- Screen lifecycle management (initialization, presentation, validation, navigation)
TActionWizard<T>for wizards without subclassing, using function delegates
Visual Grid (UVisualGrid):
TCustomVisualGridenterprise data grid with advanced features- Column binding to variant-based data with expression support for nested fields
- Custom renderers per column with full canvas access
- Filtering with multiple filter types (text matching, numeric comparisons)
- Multi-column sorting with visual indicators
- Selection modes including single cell, row, and multi-row with configurable deselection behavior
- Data sanitization hooks for transforming cell values before display
- Paging support for large datasets
Throughout the framework, dispose policies explicitly control object and memory cleanup. The TDisposePolicy enumeration defines strategies:
idpNone: No action taken, caller retains responsibilityidpNil: Set pointer to nil without freeingidpFree/idpFreeAndNil: Free object instanceidpRelease: Decrement reference count (interface objects)idpFreeMem: Free raw memory allocation
This explicitness prevents ambiguity about ownership and lifetime.
The framework embraces functional patterns through its comparer, predicate, and transformer APIs. Rather than requiring inheritance, these systems accept functions as first-class values and provide tools for composition:
// Example conceptual usage
TComparerTool<T>.Many([comparer1, comparer2, comparer3]) // Chain comparers
TPredicateTool<T>.AndMany([pred1, pred2]) // Logical AND
TPredicateTool<T>.Inverted(predicate) // NegationThe TDataRow system provides dynamic records with named field access using variants. This enables flexible data structures without compile-time schema definitions, particularly useful for data binding scenarios where column sets are determined at runtime.
Cache and collection components use synchronization primitives (TCriticalSection, TSimpleRWSync) to provide thread-safe operations. The framework follows a pattern of coarse-grained locking at operation boundaries rather than fine-grained locks at every field access, balancing simplicity and performance.
The TNotifyManyEvent and TThreadNotify types provide event systems that support multiple subscribers with explicit control over which thread executes handlers. This is critical for UI applications where certain operations must run on the main thread.
Pascal Framework is well-suited for:
- Lazarus Desktop Applications: Building rich-client applications that need advanced data grids, wizards, and form management
- Data-Intensive Applications: Applications that process large datasets with filtering, sorting, and paging requirements
- Cached Data Access: Systems that need configurable caching layers with expiration and eviction policies
- Dynamic Schema Applications: Programs working with data structures determined at runtime rather than compile time
- Cross-Platform FPC Projects: Code that needs to work across Windows, Linux, and macOS with FPC
The framework may not be appropriate for:
- Web Applications: The library is focused on desktop applications and does not address HTTP, REST, or web frameworks
- Mobile Applications: No mobile platform support or touch-optimized components
- Minimalist Projects: If you only need one or two utilities, the framework's design assumes you're building substantial applications
- Real-Time Systems: While thread-safe, the framework is not designed for hard real-time constraints or lock-free algorithms
The framework follows a layered architecture:
Foundation Layer (UMemory, UCommon):
- Basic type extensions, utilities, and memory management
- No dependencies on other framework units
- Provides building blocks used throughout higher layers
Collection Layer (UCommon.Collections):
- Built on foundation layer and
Generics.Collections - Provides functional composition tools for working with collections
- Used by data and caching layers
Data Layer (UCommon.Data):
- Depends on collection and foundation layers
- Provides data binding abstractions and filtering infrastructure
- Used by UI components for data-driven behavior
Caching Layer (UCache):
- Depends on foundation and collection layers
- Standalone subsystem with minimal coupling to other domains
- Can be used independently of UI components
UI Layer (UCommon.UI, UWizard, UVisualGrid):
- Depends on all lower layers
- Integrates with Lazarus Component Library (LCL)
- Provides visual components and application framework
Components within each layer are designed to be composable. For example, TVisualGrid uses the data binding infrastructure from UCommon.Data, caching from UCache for performance optimization, and collection tools from UCommon.Collections for filtering operations.
The framework is distributed as a Lazarus package:
- Clone or download the repository
- Open
packages/Sphere10Framework.lpkin Lazarus IDE - Compile and install the package
- Add the package to your project's required packages
program MinimalExample;
{$mode delphi}
uses
UCommon, UCommon.Collections, UMemory, Generics.Collections;
var
list: TList<Integer>;
pred: IPredicate<Integer>;
GC: TDisposables;
function IsEven(constref x: Integer): Boolean;
begin
Result := (x mod 2) = 0;
end;
begin
// Use TDisposables for automatic cleanup at scope exit
list := GC.AddObject(TList<Integer>.Create) as TList<Integer>;
list.AddRange([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
// Create predicate: filter even numbers
pred := TPredicateTool<Integer>.FromFunc(IsEven);
// Filter list in place, keeping only even numbers
TListTool<Integer>.FilterBy(list, pred);
// list now contains: [2, 4, 6, 8, 10]
// GC automatically frees list at scope exit
end.var
cmp: IComparer<Integer>;
function MyCompareFunc(constref Left, Right: Integer): Integer;
begin
Result := TCompare.Integer(Left, Right);
end;
begin
// Create comparer from function
cmp := TComparerTool<Integer>.FromFunc(MyCompareFunc);
// Use comparer
if cmp.Compare(2, 3) < 0 then
WriteLn('2 is less than 3');
end;var
cmp: IComparer<Integer>;
begin
// Chain multiple comparers - stops at first non-zero result
cmp := TComparerTool<Integer>.Many([Comparer1, Comparer2, Comparer3]);
end;The framework provides multiple extension points:
Create comparers and predicates from functions using the tool classes:
// Nested function comparer
function NestedCompareFunc(constref Left, Right: Integer): Integer;
begin
Result := TCompare.Integer(Left, Right);
end;
myComparer := TComparerTool<Integer>.FromFunc(NestedCompareFunc);
// Object method predicate
function TMyClass.MyFilterFunc(constref AItem: Integer): Boolean;
begin
Result := (AItem mod 2) = 0;
end;
myPredicate := TPredicateTool<Integer>.FromFunc(MyFilterFunc);
// Combine multiple predicates with AND logic
pred := TPredicateTool<Integer>.AndMany([pred1, pred2, pred3]);
// Combine multiple predicates with OR logic
pred := TPredicateTool<Integer>.OrMany([pred1, pred2, pred3]);TActionCache accepts function-based fetchers and size estimators:
fetcher := TActionCache<K, V>.TValueFetcher.FromNestedFunction(
function(const key: K): V
begin
// Custom fetch logic
end
);Each column in TVisualGrid can have custom rendering logic:
myColumn.Renderer := @MyCustomRenderer;
procedure MyCustomRenderer(Sender: TObject; ACol, ARow: Longint;
Canvas: TCanvas; Rect: TRect; State: TGridDrawState;
const CellData, RowData: Variant; var Handled: Boolean);
begin
// Custom drawing logic with full canvas access
Handled := True;
end;Wizard screens can dynamically modify the wizard path based on user input:
procedure TMyWizardScreen.OnNext;
begin
if SomeCondition then
UpdatePath(ptInject, [TOptionalScreen])
else
UpdatePath(ptReplaceAllNext, [TAlternativeScreen]);
end;Transform data before display in grids:
myColumn.Sanitizer := @MySanitizeFunc;
function MySanitizeFunc(const CellData, RowData: Variant): Variant;
begin
// Transform data for display
Result := FormatFloat('0.00', CellData);
end;- Cache Components:
TCacheBaseandTActionCacheare thread-safe using read-write locks. Multiple threads can safely read and write to the same cache instance. - Collections: Standard collection classes from
Generics.Collectionsare not thread-safe. Application code must provide synchronization when sharing collections across threads. - Event System:
TThreadNotifyprovides thread-safe event dispatching with automatic marshalling to target threads. Main-thread invocation is explicitly supported. - Memory Management:
TDisposablesis not thread-safe. Each thread should maintain its own disposables scope.
- Cache Lookups: O(1) average case using
TDictionaryinternally. Eviction operations vary by reap policy: O(n) for sorting-based policies, O(1) for ASAP. - Sorted Collections:
TSortedHashSet<T>maintains sorted order with O(log n) insertion and O(1) membership testing. - Grid Rendering:
TVisualGriduses virtual rendering and only paints visible rows. Paging should be used for datasets exceeding tens of thousands of rows. - Variant Operations: Data rows using variants incur overhead compared to strongly-typed records. This is acceptable for UI binding scenarios but may be unsuitable for compute-intensive inner loops.
- Explicit Dispose Policies: The framework requires explicit specification of object ownership at collection and cache boundaries. This prevents double-free and leak scenarios but requires developer attention.
- Scope Management:
TDisposablesprovides reliable cleanup even in the presence of exceptions. Each registered object is freed in reverse registration order with exception isolation. - Reference Counting: Interface-based abstractions (
IComparer,IPredicate) use reference counting. Avoid creating circular references between objects holding interfaces.
- Generic Nesting: FPC has limitations with nested generics. Some workarounds using type aliases are present in the codebase.
- TTimeSpan Precision: The FPC
TTimeSpanimplementation is accurate to millisecond scale, not tick scale like Delphi. This is sufficient for most application-level timing but unsuitable for high-precision measurement.
Pascal Framework is stable and production-ready. The library has been used in commercial applications since 2017 and has a test suite covering core functionality.
API Stability: The public APIs are considered stable. Breaking changes are avoided, though additions and deprecations may occur in future versions.
Platform Support:
- Free Pascal Compiler (FPC) 3.0 and later
- Lazarus IDE 1.8 and later
- Windows, Linux, and macOS
Testing: Unit tests exist for core utilities (UCommon, UCache, UMemory, UCommon.Collections) using FPCUnit. UI components have example applications demonstrating functionality.
Maintenance: The project is actively maintained by Herman Schoenfeld and the Sphere10 team. Issues and pull requests are accepted via GitHub.
Backward Compatibility: The project aims to maintain backward compatibility within major versions. Deprecated features will be marked before removal.
Pascal Framework is distributed under the MIT License. See the LICENSE file for complete terms.
In summary: The software is provided as-is without warranty. You are free to use, modify, and distribute it for any purpose, including commercial applications, provided the license text and copyright notice are retained.