L4G/Adonix 4GL translated into X++ statement by statement
The statement-level translation from L4G/Adonix 4GL to X++ — data-access patterns, variable and class models, transaction handling, file-based processing, and the dictionary dependency — with side-by-side code, honest accounts of what translates cleanly, what is actively harmful in a cloud runtime, and what has no counterpart at all.
What you will be able to do
Translate L4G data-access patterns (Read, Write, Rewrite, Delete, Filter, For) into X++ select, while select, update_recordset, delete_from and insert_recordset
Explain why row-at-a-time file processing is the most common performance anti-pattern carried from X3 into X++
Describe ttsbegin/ttscommit reference counting and why L4G's implicit commit model does not carry over
Rewrite an L4G entry-point override as a Chain of Command extension with next.method()
Identify which L4G instincts transfer cleanly to X++ and which must be actively unlearned
Introduction
The previous chapter mapped X3's development platform onto D365's extension model at the architecture level — what the dictionary is, how SPE/SPV/SUB layering works, and where each customisation disposition lands.
L4G data access translated into X++ selects
```xpp // X++: Read a customer record by account number CustTable custTable; select firstonly custTable where custTable.AccountNum == _accountNum;
Variables, types, and the dictionary dependency
L4G variables are typically declared with a type inferred from the dictionary field they reference, or as simple character/integer/decimal types. The dictionary provides the semantic meaning — a variable holding a customer code derives its length, format, and display label from the dictionary definition of that field.
Transaction handling: ttsbegin and ttscommit
L4G transaction control varies by context. In many cases, a Commit at the end of a processing block writes all pending changes. Rollback discards them. The model is relatively flat — a Commit inside a subroutine writes the changes made by that subroutine.
The class model: from Funprog to OO
L4G code is organised into Funprog subroutines — named blocks of procedural code called by name. There is no inheritance, no polymorphism, no encapsulation beyond the subroutine boundary. A large L4G customisation typically consists of a flat list of Funprog routines called in sequence from an entry-point script.
Entry-point overrides and screen validation translated into extensions
An SPE entry-point script in L4G fires at a defined point in an object's action lifecycle — for example, after a sales order line is validated but before it is written:
What transfers cleanly and what does not
RecId is a 64-bit surrogate key unique within a table. It replaces composite natural keys as the primary join mechanism in most foreign-key relationships. Code that reconstructs relationships from business-field matching (the way L4G references a record by its natural-key file area position) will often need a RecId join instead.
Knowledge check
Summary
The translation from L4G to X++ is semantic, not syntactic. The business intent behind every customisation survives; the expression changes completely. The largest risks in the transition are not syntax errors — they are performance regressions from row-at-a-time habits, transaction misunderstandings from implicit-commit assumptions, and…