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

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…