4. Data Types
MALDA is a dynamically-typed language with support for primitive types, arrays, and objects. Variables can hold values of any type, and types are inferred from values.
4.1 Primitive Types
MALDA supports four primitive data types:
Integer (int)
32-bit signed integers. Examples:
42
-10
0
12345
Float (float)
64-bit floating-point numbers. Examples:
3.14
-0.5
2.0
1e-5
String (string)
Sequences of characters enclosed in double quotes ("...") or single quotes ('...'). Both forms are equivalent. Examples:
"Hello, World!"
'Hello, World!'
"Line 1\nLine 2"
'Line 1\nLine 2'
"Tab\tseparated"
"It's a test" // Using double quotes when string contains single quote
'He said "hello"' // Using single quotes when string contains double quote
Boolean (bool)
Logical values: true or false.
4.2 Type System
Dynamic Typing
Variables in MALDA are dynamically typed - they can hold values of any type, and the type is determined at runtime:
var x = 10; // x is an integer
x = "Hello"; // x is now a string
x = 3.14; // x is now a float
x = true; // x is now a boolean
Type Inference
Variable types are automatically inferred from their initial values:
var name = "Alice"; // Inferred as string
var age = 25; // Inferred as integer
var pi = 3.14159; // Inferred as float
var isActive = true; // Inferred as boolean
Type Coercion
MALDA automatically converts between compatible types in certain contexts:
Integer ↔ Float Conversion
Integers are automatically converted to floats when needed:
var x = 10; // integer
var y = 3.14; // float
var sum = x + y; // x is converted to float, result is 13.14
String Concatenation
When using the + operator with strings, non-string values are converted to strings:
var name = "Alice";
var age = 25;
var message = "Name: " + name + ", Age: " + age;
// message = "Name: Alice, Age: 25"
Explicit Type Conversion
Use built-in functions for explicit type conversion (see Built-in Functions):
var num = int("123"); // Convert string to integer
var floatVal = float("3.14"); // Convert string to float
var str = string(42); // Convert number to string
4.3 Object Types
Class Instances
Objects are instances of classes. They are created using the new keyword:
class Person {
public var name;
public var age;
function Person(name, age) {
this.name = name;
this.age = age;
}
}
var person = new Person("Alice", 25); // person is an object
See Classes & Objects for more information.
Null
null is a special value representing the absence of an object. It can be assigned to any object variable:
var obj = null;
if (obj == null) {
print("Object is null");
}
null causes a runtime error.
Reference Types
Objects are reference types - when you assign an object to a variable, both variables refer to the same object:
var person1 = new Person("Alice", 25);
var person2 = person1; // person2 refers to the same object
person2.name = "Bob";
print(person1.name); // Prints "Bob" (same object)
4.4 Dictionaries
Dictionaries are mutable key–value maps with string keys and values of any type. They are useful for configuration objects, dynamic maps, and aggregating data.
Dictionary Literals
Dictionaries are created with the dict { ... } literal syntax:
var d = dict { "a": 1, "b": 2 };
print(d["a"]);
print(d["missing"] == null);
Dictionary Methods
Dictionaries are objects with built-in methods:
var d = dict { "x": 10 };
// get(key) – returns value or null
print(d.get("x")); // 10
print(d.get("missing")); // null
// set(key, value) – sets and returns the same dictionary
d.set("y", 20);
print(d["y"]); // 20
// remove(key) – removes entry and returns true/false
print(d.remove("y")); // true
// containsKey(key) – membership test
print(d.containsKey("x")); // true
// keys() – array of string keys
var keys = d.keys();
print(keys.length);
// values() – array of values
var values = d.values();
print(values.length);
// entries() – array of [key, value] pairs
var entries = d.entries();
var first = entries[0]; // [key, value]
print(first[0]); // key
print(first[1]); // value
In transpiled executables, dictionary literals are backed by Dictionary<string, object?> but expose the same behavior as in the interpreter.
4.5 Sum Types (Tagged Unions)
Sum types let you define a value that can be one of several variants, each carrying optional payload data. They are useful for structured results (e.g. success vs error) and protocol-style data.
Type Declaration
Declare a sum type with type, a type name, and one or more constructors separated by |. Each constructor has a name and optional parameters. Parameters may be name-only or carry a payload type (the same SchemaType form as schema fields: primitives, [], ?, schema or sum-type names):
type Result = Ok(value) | Err(message);
type Intent = Search(query: string) | Buy(sku: string, qty: int) | Help();
Name-only constructors stay valid (Search(query)) and remain untyped in the generated JSON Schema. Mixing typed and untyped arms in one type is allowed. Payload types are not prompt-parameter typing — write prompt greet(name), never prompt greet(name: string) — see 10. Prompts.
Constructor names (e.g. Ok, Err) become global functions that build values of that variant. The same name cannot also be a schema. validate("Result", taggedDict) checks the JSON wire shape ({"tag":"Ok","value":…}) without turning the dict into a variant — see validate(). When a payload is typed, validate and typed prompts reject JSON that does not match (for example qty: "x" against qty: int).
Constructing Values
Call a constructor like a function. It returns a tagged value carrying the arguments as payload:
var r = Ok(42); // variant Ok with payload 42
var e = Err("failed"); // variant Err with payload "failed"
var n = None(); // variant None with no payload
Matching on Variants
Use match with variant patterns: ConstructorName(binding, ...). The pattern matches if the value is that variant and binds the payload to the given names. A bare constructor name (case None:) matches that variant without binding payloads (implicit _ per slot).
type Result = Ok(value) | Err(message);
function divide(a, b) {
if (b == 0) return Err("divide by zero");
return Ok(a / b);
}
var r = divide(10, 2);
var result = match r {
case Ok(v): "ok: " + v;
case Err(msg): "error: " + msg;
};
print(result); // "ok: 5"
See 8. Control Structures for the full match syntax. The stdlib modules result and option wrap the same Ok/Err and Some/None tags without a type declaration — see 13.19.1.
4.6 Type Checking
While MALDA is dynamically typed, you can inspect types at runtime with built-in helpers (see Built-in Functions):
var x = 10;
// Preferred: type tag from typeOf()
if (typeOf(x) == "int") {
print("x is an integer");
}
// Any numeric value (integer or float)
if (isNumber(x)) {
print("x is a number");
}
// Check for null
if (x == null) {
print("x is null");
}
typeOf() returns canonical kind tags such as "int", "float", "string", "bool", "array", "dict" (for dict { }), "object" (class instances), "variant" (sum-type values), "task", "function", "class", "actor", or "null". Constructor names (e.g. Ok) are not typeOf tags — use match. Use isTag(x, "integer") during migration if older literal names are still in use.
See Also
- 5. Variables - How to declare and use variables
- 6. Arrays - Array data structures
- 14. Graphs - Graph data structures
- 11. Classes & Objects - Creating custom object types
- 13. Built-in Functions - Type conversion functions
- 10. Prompts - Typed prompt returns and schemas