Is AL your Type?

Some languages and strictly typed, others are dynamically typed, but what about AL, in this video I take a look at AL from a type perspective, check it out:

https://youtu.be/wRHroHZFQWI

In this video, Erik explores the concept of types in AL for Business Central, comparing how strongly typed and dynamically typed languages handle data. He demonstrates implicit and explicit type conversions in AL, shows how they can sometimes lead to runtime errors instead of compile-time errors, and draws comparisons with JavaScript to illustrate the differences between typing paradigms.

What Are Types?

Whenever you have a variable in AL, it has to have a type — it can be a Text, a Decimal, an Integer, a Date, and many other things. In the world of programming languages, we have two major groups:

  • Statically typed languages — the type of everything is determined at compile time
  • Dynamically typed languages — the type of something is figured out at runtime

The reason this distinction matters is that the computer itself doesn’t care about types. When you create a variable, that variable will eventually end up in memory, and the computer doesn’t really care about what’s stored there — it’s just bits.

A Quick Hardware Detour

Erik shows the motherboard from one of the first IBM PCs to illustrate this point. The motherboard has three banks of memory chips, with eight chips grouped together because it was an 8-bit computer. If you stored the letter “A” (which is decimal 65), the bits would be spread across those eight chips — each chip held 64K of a single bit, so it took eight chips to get 64K of bytes. Everything is just memory; the hardware has no concept of types.

There were some specialized military computers built with hardware typing — where the hardware itself knew what type was stored in memory to prevent errors — but that approach didn’t catch on. What we have today with PCs is general-purpose memory, and it’s up to the software to determine what’s in that memory.

Integer Memory Layout in AL

When you create three integer variables in AL, each integer is a 32-bit value (ranging from approximately −2 billion to +2 billion). That means each takes up 4 bytes, so three integers require 12 bytes on the stack. The compiler knows that the first 4 bytes hold i, the next 4 hold i2, and the last 4 hold i3.

This is also related to the concept of buffer overruns — a vulnerability where a program accesses memory outside its boundaries, potentially corrupting memory that holds the program itself and allowing execution of invalid code. But that’s a separate topic entirely.

Dynamically Typed Languages: JavaScript Comparison

To contrast AL’s approach, Erik demonstrates JavaScript in the browser console. In JavaScript, you can assign completely different types to the same variable:

a = 2;
a = "hello";

You can also create a function that behaves differently depending on the types passed in:

function add(a, b) { return a + b; }

add(2, 3);           // returns 5
add("Erik", "YouTube"); // returns "ErikYouTube"
add(3, null);         // returns 3
add("Erik", {});      // returns "Erik[object Object]"

JavaScript happily converts types on the fly. A multiplication function reveals the limits:

function mult(a, b) { return a * b; }

mult(3, 4);           // returns 12
mult(3.4, "Erik");    // returns NaN

But you don’t get that NaN error until runtime — there’s no compile-time check. This is exactly why TypeScript was invented: to add type checking on top of JavaScript.

Type Conversions in AL

Back in AL, Erik experiments with various type assignments using variables of type Text, Code, Integer, Decimal, Date, Time, DateTime, Duration, and BigText.

Text and Code

Assigning between Text and Code works fine in both directions. However, BigText cannot be implicitly converted to Text, nor can Text be assigned to BigText:

text := code;     // OK
code := text;     // OK
text := bt;       // ERROR: Cannot implicitly convert type BigText to Text
bt := text;       // ERROR

Integer and Decimal

You can assign a Decimal to an Integer and vice versa — at compile time, at least:

integer := decimal;   // Compiles OK
decimal := integer;   // Compiles OK

But there’s a catch. If you do something like:

d := 4;
i := d * 1.5;   // Result is 6.0 — fits in an integer, works fine!
i := d * 1.6;   // Result is 6.4 — RUNTIME ERROR!

The error message reveals implementation details: “Overflow under conversion of Microsoft.Dynamics.Nav.Runtime.Decimal18 value 6.4 to System.Int32.” This tells us that AL’s Integer maps to .NET’s Int32, and AL’s Decimal is implemented as Microsoft’s own Decimal18 type — not a native .NET type.

This behavior is interesting because it’s characteristic of dynamically typed languages — the code compiles successfully but can fail at runtime depending on the actual values involved.

Date Arithmetic

You can’t add two dates together (there’s no plus operator between dates), but you can add an integer to a date:

date := TODAY();
date2 := date + 7;   // Returns the date one week later

So adding 1 to a date gives you one day forward.

Duration and Date Subtraction

Subtracting one date from another and assigning it to a Duration compiles without any warnings:

duration := date2 - date;

But the result is “7 milliseconds” — not 7 days! What’s happening here involves two implicit type conversions: the date subtraction outputs an integer (7), and then that integer gets assigned to a duration (where the unit is milliseconds). Two implicit conversions happen silently, and no warning is given that the result doesn’t make semantic sense.

Time Arithmetic

The base unit for time arithmetic is milliseconds:

time1 := TIME;
time2 := time1 + 60000;  // 60,000 ms = 1 minute later

Duration behaves essentially like a formatted integer. Subtracting times gives a meaningful duration:

duration := time2 - time1;  // Shows "1 minute"
duration := time1 - time2;  // Shows "-1 minute"

This confirms that duration is a signed value — it’s fundamentally just an integer that gets formatted nicely for display.

DateTime to Date: Explicit Conversion Required

Unlike the seamless conversions above, converting a DateTime to a Date requires an explicit function call:

date := datetime;          // ERROR: Cannot implicitly convert
date := DT2DATE(datetime); // OK: Explicit conversion

This explicit conversion is required because you’re losing information — the time portion gets truncated.

The Sample Code

The project Erik is working in is a simple AL extension with a page extension on the Customer List:

pageextension 50100 CustomerListExt extends "Customer List"
{
    trigger OnOpenPage();
    begin
        Message(GenerateAwesomeMessage(TODAY()));
    end;

    local procedure GenerateAwesomeMessage(T: Date): Text
    var
        d: Integer;
        m: Integer;
        y: Integer;
    begin
        d := 3;
        m := 4;
        y := 5;
        newProcedure(d, m);
    end;

    local procedure newProcedure(var d: Integer; var m: Integer): Text
    begin
        for d := m to m * 5 do begin
            for m := d to d * 5 do begin
                if d = m then
                    exit('Test!!!');
            end;
        end;
    end;
}

This was the starting point for the demonstrations, with additional variables and experimentation added during the video.

Conclusion

AL is both a very strict language and a surprisingly relaxed one when it comes to types. It’s strongly typed at compile time, but many implicit type conversions happen seamlessly behind the scenes — some of which can lead to unexpected runtime errors rather than compile-time errors (like the decimal-to-integer overflow). Key takeaways:

  • Text ↔ Code conversions work seamlessly, but BigText requires explicit handling
  • Integer ↔ Decimal assignments compile fine, but can fail at runtime if a decimal value doesn’t fit into an integer
  • Date arithmetic uses days as the base unit, while Time arithmetic uses milliseconds
  • Duration is essentially a formatted integer representing milliseconds
  • DateTime to Date requires an explicit conversion function (DT2DATE) because information is lost
  • It’s always a good idea to be explicit about type conversions to ensure errors are caught at compile time rather than runtime

Understanding how AL handles types under the hood — and where it silently performs conversions — helps you write more robust code and avoid subtle runtime bugs in your Business Central extensions.