Turbo Pascal (7.0)
This assessment applies the coupling account — coupling as access to a shared resource, and the coupling floor [1], [2] — and the knowledge-embodiment theorem [3]; the language is assessed against its official reference [4].
1 Assessment
This page assesses Turbo Pascal in five parts: core coupling — what the language’s own constructs force; ecosystem coupling — what the surrounding platform, tooling, and community cost; engineering readiness — whether the artifact can be reproduced, verified, observed, worked in quickly, connected to other languages, and governed openly; the profile — the unscored facts a score cannot express; and the scoring detail — the mechanism marks and the arithmetic behind every score. A ✅ bullet marks a construct that helps. A ⚠️ or ❌ bullet marks a cost; the scoring model does not price every cost named here.
Core coupling 39/100 · Ecosystem coupling 54/100 · Engineering readiness 42/100
1.1 Core coupling
1.1.1 Type
- ✅ Strong static typing is enforced at compile time: Every variable, parameter, and function result has a declared type, and an assignment between incompatible types is rejected
- ✅ Enumerated and subrange types state a value domain:
type TDay = (Mon, Tue, Wed, Thu, Fri)and a subrange such as1..31narrow the domain in the declaration - ✅ A typed pointer names what it addresses:
^Tis a pointer toT, so an assignment between pointers of different types is an error - ❌ A variant record is an unchecked union:
record ... case Tag: T of ... endnames the alternatives, but the compiler does not require the tag to be tested before a field is read (T4) - ❌ No generics: A container is written for one element type, or it stores
Pointervalues and casts them, so reuse across types is untyped (T5) - ✅ A unit is the namespace and a real module boundary: The
interfacesection is what ausesclause sees, and theimplementationsection is private to the unit
1.1.2 Construction
- ✅ A
usesclause states each dependency: The compiler rejects a call into a unit that is not listed, so the dependency edge is written in the source - ✅
interfaceandimplementationseparate the contract from the code: A caller compiles against the declarations only - ✅ An object declares its own construction and destruction: An
objecttype declaresconstructor Initanddestructor Done, andNew(P, Init)runs the constructor together with the allocation - ✅ Unit
initializationandfinalizationrun in dependency order: A unit’s startup code runs before the program body and its shutdown code runs after it - ❌ Mutability is the default: A
varis writable and reassignable, and only aconstmarks a value fixed (C2) - ⚠️ A class hierarchy couples a derived object to its base: Object-oriented programming arrived in version 5.5, and an
objecttype derives from one base, so shared behaviour is reached through that single-inheritance chain
1.1.3 Memory
- ❌ Every heap allocation is manual:
NewandDispose, orGetMemandFreeMem, are the ways to use the heap, and the programmer writes the matching release (M1) - ❌ Nothing runs when a variable leaves its scope: There is no release tied to scope, and a destructor is a method the programmer must call (M3)
- ❌ No ownership model: A typed pointer may alias, dangle, or be disposed twice, and the compiler does not track it (M4)
- ✅ The run-time library owns the heap:
New,Dispose,Mark, andReleasegive a defined heap with a stack-like mark and release discipline - ✅ A
Stringvalue carries its own length: AString[255]short string stores its length in the first byte, so a string operation needs no terminator - ⚠️ Real-mode addressing bounds the heap: In the 16-bit memory models the heap lies in the data segment, so the model’s segment limit caps one allocation
1.1.4 Error
- ❌ The default failure path is a halt or an error code: An unhandled run-time error stops the program with a run-time error number, and an I/O failure is reported through
IOResultwhen checking is off (Er1) - ❌ No composable result type: A failure cannot be carried in the type of a function’s result, so the caller learns of it from an error code or an exception (Er4)
- ✅
try/except/finallyis a structured path: Version 7 added exception handling, and thefinallyblock runs on every exit from the statement - ✅
raisenames the failure:raise EDivByZero.Createraises an object from theSysUtilsexception hierarchy - ⚠️ The exceptions are unchecked: Nothing in a signature states which exceptions a routine may raise, so the recovery path depends on a convention
- ⚠️ Two error styles coexist:
{$I-}withIOResultchecks one call at a time, whiletry/exceptcovers a block, so a program mixes both
1.1.5 Concurrency
- ❌ The language has no concurrency construct: There is no thread type, no task, and no synchronisation primitive; concurrent work in DOS is expressed with an interrupt procedure (K5)
- ⚠️ The only shared state is a global variable: With no concurrency construct in the language, a global variable is what the main code and an interrupt procedure both reach, and nothing guards the access
- ⚠️ A thread-local variable needs a successor compiler:
threadvargives a variable one copy per thread in Delphi and Free Pascal, but Turbo Pascal creates no thread and the DOS target has none - ⚠️ An interrupt procedure must be declared as such: The
interruptdirective saves the registers, and the protection is a declaration rather than a checked protocol - ⚠️ Reentrancy is the programmer’s concern: An interrupt procedure that calls a library routine can corrupt state the interrupted code was using
- ⚠️ Cooperative concurrency came from an add-on: A terminate-and-stay-resident program or a library simulated multitasking outside the language
1.1.6 Platform
- ❌ The main target is 16-bit DOS: The compiler produces real-mode DOS executables, and version 7 was the last release of the line (P3)
- ❌ A compiled unit is a private object format: A
.TPUfile is Turbo Pascal’s own unit format, and no other language’s linker consumes it - ✅ Free Pascal and Delphi compile the same source: Both accept Turbo Pascal units, so the source is not frozen to the original compiler
- ✅ The compiler is a self-contained toolchain: One executable compiles a program, and the IDE runs the edit-compile-run cycle
- ⚠️ The memory model is a compile-time choice:
{$M}selects tiny, small, compact, large, or huge, and the model bounds the code and the data segments - ⚠️ Real-mode addressing shows in the source:
nearandfarpointers and segment arithmetic appear once a program leaves the small model
1.1.7 Trade-offs (not coupling)
- The one-pass compiler is a deliberate speed choice: Compilation is nearly instant, and the price is a modest optimiser
- The integrated IDE is the intended way to work: The compiler, the editor, and the debugger are one program
- The 16-bit limit belongs to the target machine: The segment size is a property of the processor mode, not only of the language
- Turbo Vision is supplied and not required: The text-mode GUI library is an optional dependency of the program
- The
gotostatement remains in the language: A jump is available for the case where a structured form is awkward - Pascal is a teaching language by design: The readable syntax and the strong typing were written for instruction
1.2 Ecosystem coupling
1.2.1 Performance
- ✅ The compiler emits native 8086 machine code: There is no interpreter and no run-time compiler between the source and the processor
- ✅ Compilation is very fast: The one-pass compiler makes the edit-compile-run cycle take seconds
- ✅ A compiled program starts immediately: A DOS
.EXEneeds no runtime initialisation beyond the loader - ⚠️ The optimiser does little global work: The algorithm and the data layout dominate the result
- ⚠️ The 16-bit target caps the work: The real-mode segment limit bounds the data one program can address
- ⚠️ A far pointer costs extra cycles: A segment override and a far access element are slower than a register operation
1.2.2 Ecosystem
- ❌ The ecosystem is legacy and discontinued: Borland ended Turbo Pascal after version 7 in favour of Delphi, so the tool and package field is historical (Ec3)
- ✅ Free Pascal, Delphi, and Virtual Pascal continue the language: Each is a compatible implementation, so much Turbo Pascal code still compiles today
- ✅ A large body of published material exists: The era’s books, tutorials, and the Turbo Vision source remain available
- ✅ The compiler is a single small program: Nothing else must be installed to build a program
- ⚠️ Reuse is by source: A unit is shared as
.PASsource rather than as a resolved package with a version - ⚠️ The tooling runs in DOS or an emulator: The IDE and the debugger need a DOS machine or an emulator on a current computer
1.2.3 Evolution
- ❌ The language is discontinued: Borland ended the line in 1995 and moved it to Delphi and Object Pascal, so no version followed 7.0 (V3)
- ❌ The definition is frozen at 7.0: The language does not change, so a missing feature or a defect is permanent (V2)
- ✅ The reach of the tool was historically large: Over 400 000 copies were sold in the first two years, which made Pascal accessible to a generation of programmers
- ✅ The successors accept the source: Delphi and Object Pascal keep the unit, the static typing, and most of the syntax
- ✅ A port is an adjustment rather than a rewrite: Free Pascal compiles a unit with little change outside the platform-specific code
- ⚠️ The platform-specific part is real work: DOS interrupts, direct video memory,
inlinemachine code, and.TPUlinkage have no direct counterpart on a current target
1.2.4 Domain
- ✅ Structured programming instruction is the native domain: The readable syntax and the strong typing were designed for teaching
- ✅ DOS application and tool development fit the target: A compiled
.EXEneeded no runtime and ran on any PC of the era - ✅ Turbo Vision covers text-mode GUI work: The bundled library provides windows, menus, and dialogs on a character screen
- ✅ A small number-crunching utility is a natural use: A scientific or engineering program was a common Turbo Pascal program
- ⚠️ New development does not choose it: The domain today is legacy maintenance and education
- ⚠️ Hardware access sits outside the language: A program that drives a device uses the BIOS, a DOS interrupt, or
inlinecode
1.2.5 Learning
- ✅ The syntax is small and readable:
begin/end,var,procedure,if/then/else, andfor/tocover the language - ✅ The compiler reports an error immediately: The short edit-compile-run cycle makes a mistake cheap to find
- ✅ The IDE integrates a debugger: Step, watch, and breakpoint are keystrokes away
- ⚠️ Pointers and manual memory are the hard part: The syntax is gentle and the heap discipline is not
- ⚠️ Real-mode addressing is a second subject: Near and far pointers and segments must be learned for system work
- ⚠️ The semicolon rule catches a beginner: A semicolon separates statements, so the last statement of a block takes none
1.2.6 Syntax
- ❌ The syntax is verbose:
begin/end,unit/interface/implementation,try/except/end, and every declaration keyword are spelled out (S1) - ✅ Keywords are words rather than symbols:
if,then,do,of, andendcarry the structure - ✅ Blocks are bracketed consistently:
begin/endcloses every compound statement, including the body of a procedure - ✅ A declaration names the variable before its type:
var Count: Integerreads in one direction - ⚠️ A statement separator, not a terminator: The semicolon rule is a rule to learn rather than a decoration
- ⚠️ A nested field needs its qualifier or a
with: Reading a record inside a record is verbose without thewithstatement
1.2.7 Testing
- ❌ There is no test framework in the toolchain: A test is a program that runs and compares, and nothing in the distribution runs a suite (G1)
- ❌ The debugger is the era’s tool: The IDE steps and watches variables, with no assertion or coverage support (G2)
- ✅ A failed run-time check reports the detail: The run-time library prints the error number, the address, and the register contents
- ✅ A check is a few lines of Pascal: A comparison and a halt write a test without a framework
- ⚠️ A test suite is a program the programmer writes: No runner, no fixture loader, and no report come with the toolchain
1.2.8 Security
- ✅ The type system rejects a mismatch at compile time: A pointer to one type cannot take a pointer to another without a written cast
- ✅ The dangerous cast is one written site:
Pointerand a type cast put the risk where the programmer wrote it - ✅ A check can be switched on:
{$R+}adds range checking to an array index and to a subrange assignment - ⚠️ Manual memory management admits the classic faults: An overrun and a use-after-free are possible, and the compiler does not check them
- ⚠️
{$I-}disables an I/O check: After that directive a failed file operation must be tested throughIOResult - ⚠️
inlinemachine code bypasses the type system: Aninline(...)statement writes raw opcodes into the compiled program
1.3 Engineering readiness
1.3.1 Reproducibility
- ❌ Builds are not byte-reproducible by default.
- ❌ Dependencies resolve without a lock by default.
1.3.2 Supply chain
- ⚠️ Published packages carry no signed provenance by default.
1.3.3 Observability
- ⚠️ No stable tracing and metrics standard.
- ⚠️ The standard library has no structured logging.
1.3.4 Feedback
- ⚠️ No fast in-editor analysis.
1.3.5 Interop
- ⚠️ No maintained portable target.
1.3.6 Governance
- ⚠️ One vendor decides the language’s direction.
- ⚠️ The specification is not public.
1.4 Profile
Unscored, and kept separate from every score on purpose. The profile records what a mechanism price cannot express: what a running program costs, how well a coding model and an agent work in the language, and whether a safety-critical path exists.
1.4.1 Energy and cost
What this axis asks: what does a running program cost in energy and time?
1.4.2 AI and agent readiness
What this axis asks: how well does a coding model and an agent work in this language?
- ❌ Sparse training data: models write it unreliably [8].
1.4.3 Safety certification
What this axis asks: is there a certified or standards-qualified path to a safety-critical system?
- ⚠️ No certified or standards-qualified path for safety-critical systems.
1.5 Scoring detail
1.5.1 Core coupling Dimensions
| Dimension | Mechanism | Description | Present | Cost |
|---|---|---|---|---|
| Type | T1 |
dynamic no static check | — | 0 |
T2 |
weak static | — | 0 | |
T3 |
dynamic/duck dispatch | — | 0 | |
T4 |
no sum types | ✅ | −4 | |
T5 |
no generics | ✅ | −4 | |
T6 |
no namespaces | — | 0 | |
T7 |
type erasure/reflection | — | 0 | |
T8 |
operator overloading | — | 0 | |
T9 |
inheritance required for polymorphism | — | 0 | |
T10 |
no null-safety | — | 0 | |
T11 |
no module boundary | — | 0 | |
T12 |
no effect tracking | ✅ | −8 | |
T13 |
async/sync colouring | — | 0 | |
T14 |
unchecked foreign boundary | ✅ | −4 | |
| Type subtotal | −15 (−20) | |||
| Construction | C1 |
global mutable state by default | — | 0 |
C2 |
no immutability by default | ✅ | −4 | |
C3 |
constructors coupled to allocation | — | 0 | |
C4 |
no constructor/validation | — | 0 | |
| Construction subtotal | −4 | |||
| Memory | M1 |
manual deallocation | ✅ | −15 |
M2 |
GC non-deterministic pauses | — | 0 | |
M3 |
no RAII | ✅ | −4 | |
M4 |
no ownership model | ✅ | −8 | |
| Memory subtotal | −15 (−27) | |||
| Error | Er1 |
no structured path | ✅ | −15 |
Er2 |
unchecked exceptions only | — | 0 | |
Er3 |
errors ignored/non-exhaustive | — | 0 | |
Er4 |
no composable result type | ✅ | −4 | |
| Error subtotal | −15 (−19) | |||
| Concurrency | K1 |
GIL/no parallel | — | 0 |
K2 |
shared mutable state default | — | 0 | |
K3 |
manual sync no race detection | — | 0 | |
K4 |
no structured concurrency | — | 0 | |
K5 |
no primitives | ✅ | −4 | |
| Concurrency subtotal | −4 | |||
| Platform | P1 |
cannot function outside one vendor/domain | — | 0 |
P2 |
single-vendor but other uses | — | 0 | |
P3 |
legacy/discontinued platform | ✅ | −8 | |
| Platform subtotal | −8 |
Core Dimensions score: 39/100 — total cost = −61 (per-dimension subtotals are capped at −15; an uncapped cost is shown in parentheses where the cap bites).
1.5.2 Ecosystem coupling Dimensions
| Dimension | Mechanism | Description | Present | Cost |
|---|---|---|---|---|
| Performance | R1 |
interpreted no AOT/JIT | — | 0 |
R2 |
GC pauses | — | 0 | |
R3 |
JIT warmup | — | 0 | |
| Performance subtotal | 0 | |||
| Ecosystem | Ec1 |
no package manager | — | 0 |
Ec2 |
fragmented tooling | — | 0 | |
Ec3 |
small/niche/discontinued | ✅ | −8 | |
Ec4 |
proprietary | — | 0 | |
Ec5 |
platform-locked tooling | — | 0 | |
Ec6 |
monoculture | — | 0 | |
Ec7 |
legacy burden | — | 0 | |
| Ecosystem subtotal | −8 | |||
| Evolution | V1 |
breaking changes | — | 0 |
V2 |
frozen | ✅ | −4 | |
V3 |
discontinued | ✅ | −15 | |
V4 |
glacial standards | — | 0 | |
| Evolution subtotal | −15 (−19) | |||
| Domain | D1 |
domain/vendor lock-in | — | 0 |
D2 |
poor domain fit | — | 0 | |
D3 |
framework forces architecture | — | 0 | |
| Domain subtotal | 0 | |||
| Learning | L1 |
steep | — | 0 |
L2 |
large surface/multi-paradigm | — | 0 | |
L3 |
sparse docs | — | 0 | |
| Learning subtotal | 0 | |||
| Syntax | S1 |
verbose | ✅ | −8 |
S2 |
inconsistent | — | 0 | |
S3 |
punctuation-heavy | — | 0 | |
| Syntax subtotal | −8 | |||
| Testing | G1 |
weak tooling | ✅ | −8 |
G2 |
poor debugging | ✅ | −8 | |
G3 |
runtime-only errors | — | 0 | |
| Testing subtotal | −15 (−16) | |||
| Security | Q1 |
memory-unsafe | — | 0 |
Q2 |
dynamic/weak typing risk | — | 0 | |
Q3 |
known vulnerability/injection | — | 0 | |
| Security subtotal | 0 |
Meta Dimensions score: 54/100 — total cost = −46 (per-dimension subtotals are capped at −15; an uncapped cost is shown in parentheses where the cap bites).
1.5.3 Engineering readiness Dimensions
| Dimension | Mechanism | Description | Present | Cost |
|---|---|---|---|---|
| Reproducibility | Y1 |
build not reproducible | ✅ | −8 |
Y2 |
dependencies not pinned | ✅ | −8 | |
| Reproducibility subtotal | −15 (−16) | |||
| Supply chain | Y3 |
no signed provenance | ✅ | −8 |
Y4 |
registry without namespacing | — | 0 | |
| Supply chain subtotal | −8 | |||
| Observability | Y5 |
no stable tracing standard | ✅ | −8 |
Y6 |
no structured logging | ✅ | −4 | |
| Observability subtotal | −12 | |||
| Feedback | Y7 |
slow build | — | 0 |
Y8 |
no fast in-editor analysis | ✅ | −4 | |
| Feedback subtotal | −4 | |||
| Interop | Y9 |
no foreign-function interface | — | 0 |
Y10 |
no portable target | ✅ | −4 | |
| Interop subtotal | −4 | |||
| Governance | Y11 |
single-vendor governance | ✅ | −8 |
Y12 |
closed specification | ✅ | −8 | |
| Governance subtotal | −15 (−16) |
Readiness Dimensions score: 42/100 — total cost = −58 (per-dimension subtotals are capped at −15; an uncapped cost is shown in parentheses where the cap bites).
Verdict: Turbo Pascal’s core score is held down by the mechanisms of its era rather than by a missing idea: the heap is manual, no release is tied to scope, and a failure is a run-time error number or an unchecked exception. Its one strong structural feature is the unit, where interface and implementation draw a real boundary and the uses clause writes each dependency into the source. Main strengths: strong static typing, near-instant native compilation, a gentle readable syntax, and a live path to Free Pascal and Delphi. Main weaknesses: no generics, unchecked variant records, no concurrency construct in the language, and a 16-bit target whose toolchain, ecosystem, and platform are all historical.
The formal development. The definitions, theorems, and proofs behind this page are published in:
- On the Nature of Coupling: All Coupling as Shared Resource Access — Zenodo, 2026 — doi:10.5281/zenodo.21925122
- Coupling as a Consequence of Cohesion: Decomposition, the Coupling Floor, and Domain-Imposed Coupling — Zenodo, 2026 — doi:10.5281/zenodo.21925201
- On the Nature of Cohesion: Cohesion as Correctness Relative to a Partitioning Rule — Zenodo, 2026 — doi:10.5281/zenodo.21703193