Data Types and Coercion
Lingo's value types - integers, floats, strings, symbols, VOID, points, rects, colors - plus type predicates and conversion rules.
Type inventory
| Type | Literal / constructor | ilk() symbol | Notes |
|---|---|---|---|
| Integer | 42, -7 | #integer | Signed 32-bit. Overflow wraps. |
| Float | 3.14, 1e6, float(x) | #float | Double precision internally; display digits governed by the floatPrecision. |
| String | "hello" | #string | 1-indexed; case-insensitive comparisons. |
| Symbol | #idle, symbol("idle") | #symbol | Interned name; constant-time equality; case-insensitive. |
| Boolean | TRUE / FALSE | #integer | Just integers 1 and 0. Any nonzero integer is true in conditions. |
| VOID | VOID | #void | The empty value; returned for missing things. Test with voidP(). |
| List | [1, 2, 3] | #list | Linear list; see Lists. |
| Property list | [#a: 1, #b: 2] | #propList | Ordered key/value pairs. Empty literal is [:]. |
| Point | point(10, 20) | #point | p.locH, p.locV; supports arithmetic. |
| Rect | rect(l, t, r, b) | #rect | Also rect(point1, point2); r.left/top/right/bottom/width/height. |
| Color | rgb(255, 0, 0), rgb("#FF0000"), paletteIndex(35) | #color | Two color models: direct RGB and palette index. |
| Date | date(2004, 1, 31) | #date | MX 2004 date object. |
| Vector | vector(x, y, z) | #vector | 3D vector for Shockwave 3D. |
| Image | image(w, h, depth), member(m).image | #image | Imaging Lingo bitmap surface (D8+). |
| Object | new(script "Foo") | #instance / #object | Child object, behavior instance, Xtra instance, window, etc. |
| Member/Sprite refs | member(1), sprite(1) | #member, #sprite | References, not copies. |
Constants
| Constant | Value |
|---|---|
TRUE / FALSE | 1 / 0 |
VOID | empty value |
EMPTY | "" (empty string) |
RETURN | carriage return character (char 13) |
ENTER | numeric-keypad Enter (char 3 on classic Mac mapping) |
TAB | tab character (char 9) |
BACKSPACE | backspace character (char 8) |
QUOTE | the " character (the only way to embed quotes in a string literal) |
SPACE | " " |
PI | 3.14159265... |
put "She said" && QUOTE & "hi" & QUOTE -- She said "hi"
line1 = "a" & RETURN & "b"Type predicates
integerP(x) floatP(x) stringP(x) symbolP(x)
listP(x) objectP(x) voidP(x) pictureP(x)
ilk(x) -- returns the type symbol, e.g. #propList
ilk(x, #list) -- TRUE if x is that ilk (lists: #list matches both kinds)listP() is TRUE for linear lists, property lists, rects, and points. objectP() is TRUE for anything object-like and FALSE for VOID; old code frequently uses objectP(gConn) as an "is initialized" test.
Truthiness
A condition expects an integer:
- Integer 0 is FALSE, any other integer is TRUE.
- TRUE/FALSE are literally 1/0, and
the ... ofboolean properties return 1/0. - Strings, VOID, floats, and lists are not silently truthy:
if "abc" thenraises a type error in classic Lingo. Emulators must not adopt JavaScript truthiness. Comparisons produce integers, soif x > 0 thenis the normal shape.
Numeric coercion
integer(3.7) -- 4 (rounds to nearest, .5 rounds away from zero)
integer("42") -- 42
integer("42x") -- VOID in MX 2004 (strict trailing-garbage rule; D6 returned 0)
float("3.5") -- 3.5000
float(7) -- 7.0000
string(3.5) -- "3.5000" (uses the floatPrecision, default 4)
value("3 + 4") -- 7 : value() EVALUATES the string as a Lingo expression
value("[1,2]") -- the list [1, 2]
value("abc") -- VOID (not a valid expression)Behavior worth memorizing:
- Integer arithmetic stays integer (
5 / 2 = 2); introduce a float to promote (5 / 2.0 = 2.5). the floatPrecision(default 4) affects only string formatting, not stored precision.value()is a full expression evaluator, which makes it powerful and dangerous: it resolves symbols and can execute function-like expressions. For parsing server data, well-written code usesinteger()/float()instead.- String-to-number coercion tolerates leading/trailing spaces. Embedded control characters (like the
chr(1)/chr(2)delimiters in Habbo-style network protocols) make MX 2004integer()/float()return VOID rather than parsing a prefix; robust packet parsers strip delimiters before coercion. Older players were more lenient, so verify against the version you target (see Strings and chunks).
Character coercion
charToNum("A") -- 65
numToChar(65) -- "A"
chr(65) -- "A" (legacy alias seen in old code)Classic Director strings are single-byte (MacRoman on Mac-authored, Windows-1252 on Windows-authored content); Unicode arrived only in Director 11. Byte-oriented protocol code (numToChar(1) as a packet separator) relies on this.
Symbols
state = #idle
if state = #idle then state = #running
put string(#idle) -- "idle"
put symbol("idle") -- #idle- Symbols are interned once and compared by identity, making them the standard choice for enums, message names (
sendSprite(5, #reset)), and property list keys. - A symbol must start with a letter;
symbol()converts arbitrary strings (invalid ones return VOID). - Symbol comparisons are case-insensitive (
#Idle = #idleis TRUE).
Points, rects, colors
p = point(10, 20)
p = p + point(5, 5) -- point(15, 25); arithmetic is element-wise
r = rect(0, 0, 100, 50)
put r.width -- 100
inflated = r + rect(-2,-2,2,2) -- grow by 2px each side
put inside(p, r) -- TRUE/FALSE
u = union(r1, r2)
x = intersect(r1, r2) -- rect(0,0,0,0) when disjoint
m = map(r, fromRect, toRect) -- proportional rect/point mapping
c = rgb(255, 128, 0)
put c.red, c.green, c.blue
i = paletteIndex(35) -- palette-relative colorPalette-index colors matter for old 8-bit content: a paletteIndex color resolves through the active palette at draw time, so the same sprite can change color when the frame palette changes. Emulators must keep the distinction between rgb() and paletteIndex() colors instead of flattening both to RGB at parse time (see Native rendering for how the native compositor preserves index provenance).