Learn MALDA
A short course through the core language. Each lesson is one idea, one runnable file, and one chapter section. The numbered chapters stay the reference; this page is the spine.
The same order is Desktop Start With MALDA, the Web IDE student track, and Start Here.
How to run a file
How to get MALDA
You need the malda command, or an IDE that already has it:
- Windows — download
malda-<version>-win-x64.zipfrom GitHub Releases (CLI + Desktop IDE; no separate .NET install). Unzip and runMaldaDesktop.bat, then Start With MALDA. For the CLI:malda.bat, or addbin/maldato yourPATH. Details: root README “Install without cloning”. - Linux zip — download
malda-<version>-linux-x64.zip, unzip, and run./malda. Optional: addbin/maldato yourPATH. - From source — clone this repo, then from the root:
dotnet run --project MaldaLang -- Examples/Basics/hello_world.malda. - No install — the Web IDE playground (browser; not Desktop parity).
The interpreter is the default. After malda is on your path, from the repo root:
malda Examples/Basics/hello_world.malda
That prints Hello, World! and exits. You do not need compile, an API key, or a project scaffold for this path. Diagnose without running with malda check. Editors: Desktop IDE is the Windows reference; Web IDE is a browser playground (not Desktop parity); VS Code + LSP is the cross-platform editor. The full command index is 2. Tools & Tooling.
Prefer namespaced calls (io.print, math.sqrt, str.upper). Flat print still runs.
Lesson 0 — First Look (optional)
If you already program, start here. -> Review binds the prompt to the schema. Without await, the call is a rendered template (no API key). validate("Review", …) is the same check await would run on the model JSON. Schema syntax: 4.6. Same file: Examples/Basics/first_look.malda.
schema Review {
summary: string;
issues: string[];
}
prompt codeReview(code, language) -> Review {
system: "You are an expert reviewer of {language}.",
user: "Review this {language} code:\n\n{code}"
}
var rendered = codeReview("function add(a, b) { return a + b; }", "javascript");
io.print(rendered.user);
var checked = validate("Review", {
"summary": "Looks fine",
"issues": []
});
if (checked.ok) {
io.print("schema ok: " + checked.data.summary);
} else {
io.print("schema failed: " + checked.error);
}
summary to "Needs work" and re-run. What is the last printed line?
Answer
schema ok: Needs work
schema Item { name: string; }. Validate { "name": "pen" }. If it succeeds, print schema ok: plus the name. Last line: schema ok: pen.
Answer
schema Item {
name: string;
}
var checked = validate("Item", {
"name": "pen"
});
if (checked.ok) {
io.print("schema ok: " + checked.data.name);
} else {
io.print("schema failed: " + checked.error);
}
Lesson 1 — Hello World
One call writes one line. Chapter: 12.1 Console output. Same file: Examples/Basics/hello_world.malda.
io.print("Hello, World!");
"Hello, MALDA!" and re-run.
Answer
The program prints Hello, MALDA! and exits.
Good morning. on the first line and Ready. on the second. Last line: Ready.
Answer
io.print("Good morning.");
io.print("Ready.");
Lesson 2 — Variables and arithmetic
var declares a binding. The type comes from the value. $"Sum: {sum}" inserts the expression into the string; "Sum: " + string(sum) is the same. Chapter: 5.1 Variable declaration. Same file: Examples/Basics/variables_arithmetic.malda.
var x = 10;
var y = 20;
var sum = x + y;
var product = x * y;
io.print($"Sum: {sum}");
io.print($"Product: {product}");
10 to 7 and re-run. What is the new sum?
Answer
27 (7 + 20).
var width = 4 and var height = 5. Print the product as Area: 20. Last line: Area: 20.
Answer
var width = 4;
var height = 5;
io.print("Area: " + string(width * height));
Lesson 3 — Conditionals
if / else if / else choose a branch. Chapter: 8.1 Conditional statements. Same file: Examples/Basics/conditionals.malda.
var age = 18;
if (age >= 18) {
io.print("You are an adult");
} else {
io.print("You are a minor");
}
var score = 85;
if (score >= 90) {
io.print("Grade: A");
} else if (score >= 80) {
io.print("Grade: B");
} else if (score >= 70) {
io.print("Grade: C");
} else {
io.print("Grade: F");
}
age to 16. What prints first?
Answer
You are a minor
var temp = 3. If temp < 0 print ice, else if temp < 10 print cold, else print warm. Last line: cold.
Answer
var temp = 3;
if (temp < 0) {
io.print("ice");
} else if (temp < 10) {
io.print("cold");
} else {
io.print("warm");
}
Lesson 4 — While loop
A while loop repeats while the condition is true. Update the loop variable yourself. Chapter: 8.2 While. Same file: Examples/Basics/while_loop.malda.
var i = 1;
while (i <= 10) {
io.print($"Count: {i}");
i = i + 1;
}
io.print("Done!");
5. How many Count: lines print?
Answer
Five lines: Count: 1 through Count: 5.
while to print 2, then 4, then 6, then done. Last line: done.
Answer
var n = 2;
while (n <= 6) {
io.print(n);
n = n + 2;
}
io.print("done");
Lesson 5 — For loop
A C-style for packs init, condition, and increment. Chapter: 8.2 For. Same file: Examples/Basics/for_loop.malda.
for (var i = 1; i <= 10; i = i + 1) {
io.print($"Number: {i}");
}
io.print("Loop finished!");
5. What is the first Number: line?
Answer
Number: 5
for to print 10, then 9, then 8. Last line: 8.
Answer
for (var i = 10; i >= 8; i = i - 1) {
io.print(i);
}
Lesson 6 — Functions
The only function keyword is function. fn and def are syntax errors. Chapter: 9.1 Function declaration. Same file: Examples/Basics/functions.malda.
function add(a, b) {
return a + b;
}
var result1 = add(5, 3);
io.print($"5 + 3 = {result1}");
var result2 = add(10, 2);
io.print($"10 + 2 = {result2}");
io.print(add(1, 1)); after the existing prints. What is the last line?
Answer
2
function greet(name) that returns "Hi, " + name. Print greet("Ada"). Last line: Hi, Ada.
Answer
function greet(name) {
return "Hi, " + name;
}
io.print(greet("Ada"));
Lesson 7 — Lists and indexing
Arrays are zero-indexed and grow with append. A counted for walks indexes; for-in walks values. Chapter: 6. Arrays. Same file: Examples/Basics/lists_indexing.malda.
var scores = [88, 92, 75];
io.print("first=" + string(scores[0]));
io.print("length=" + string(scores.length));
scores.append(80);
io.print("after append length=" + string(scores.length));
var last = scores.pop();
io.print("popped=" + string(last));
io.print("length after pop=" + string(scores.length));
var total = 0;
for (var i = 0; i < scores.length; i = i + 1) {
io.print("scores[" + string(i) + "]=" + string(scores[i]));
total = total + scores[i];
}
var walked = 0;
for (var score in scores) {
walked = walked + 1;
}
io.print("sum=" + string(total));
io.print("for-in count=" + string(walked));
io.print("lists ok");
append, append another score 100 and do not pop it. What is the new printed sum?
Answer
355 (88 + 92 + 75 + 100). The original file pops the appended 80 before summing.
var nums = [1, 2, 3]. Append 4. Print last= plus the last value. Last line: last=4.
Answer
var nums = [1, 2, 3];
nums.append(4);
io.print("last=" + string(nums[nums.length - 1]));
Lesson 8 — Complete starter
Variables, a for-in loop, a function, and an if in one linear program — now that lists are familiar. Catalog note: 35.1. Same file: Examples/Basics/complete_starter_program.malda.
var cart = [12, 18, 7];
var total = 0;
for (var item in cart) {
total = total + item;
}
function describeTotal(amount) {
if (amount >= 30) {
return "big order";
}
return "small order";
}
io.print($"Items: {cart.length}");
io.print($"Total: {total}");
io.print($"Summary: {describeTotal(total)}");
7 to 4. Is it still a big order?
Answer
Yes. The total becomes 34, which is still >= 30.
30 to 40. What is the summary?
Answer
small order (37 is below 40).
var prices = [5, 5, 5] with for-in and print Total: 15. Last line: Total: 15.
Answer
var prices = [5, 5, 5];
var total = 0;
for (var price in prices) {
total = total + price;
}
io.print("Total: " + string(total));
Lesson 9 — Input
io.input reads one line from stdin and always returns a string. Convert it if you need a number. This snippet waits for a keyboard, so it is not marked runnable here. Chapter: 12.2 Console input. Same file: Examples/Basics/input_example.malda.
var name = io.input("Enter your name: ");
var ageStr = io.input("Enter your age: ");
var age = int(ageStr);
io.print($"Hello, {name}. You are {age} years old.");
At end of input every later io.input returns "", not null. Prefer toIntOr(raw, fallback) over bare int(...) when the line may be blank.
io.input return at end of file?
Answer
An empty string "", not null.
io.input("City: ") into city and print You live in plus that string. This waits for a keyboard, so it is not marked runnable here. At end of file city is "".
Answer
var city = io.input("City: ");
io.print("You live in " + city);
Lesson 10 — Dictionaries
A dict maps string keys to values. Index to read or write; containsKey, keys, and values inspect the map. Chapter: 4.4 Dictionaries. Same file: Examples/Basics/dictionary_example.malda.
var d = dict { "a": 1, "b": 2 };
io.print("a = " + d["a"]);
io.print("b = " + d["b"]);
d["c"] = 3;
io.print("c = " + d["c"]);
io.print("has b: " + d.containsKey("b"));
io.print("has missing: " + d.containsKey("missing"));
var keys = d.keys();
var values = d.values();
io.print("keys length: " + keys.length);
io.print("values length: " + values.length);
d["d"] = 4; and print it. What is keys.length after that?
Answer
4
"ok" set to true. Print ok= plus the value. Last line: ok=true.
Answer
var flags = dict { "ok": true };
io.print("ok=" + flags["ok"]);
Lesson 11 — Recursion
A function may call itself. Write a base case that stops, then a smaller argument. Chapter: 9.4 Recursion. Same file: Examples/Basics/recursion.malda.
function factorial(n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
function countdown(n) {
if (n <= 0) {
io.print("liftoff");
return 0;
}
io.print(n);
return countdown(n - 1);
}
io.print("factorial(5)=" + string(factorial(5)));
io.print("countdown from 3:");
countdown(3);
io.print("recursion ok");
factorial(7). What is the value?
Answer
5040
function sumTo(n) that returns 1 when n <= 1, else n + sumTo(n - 1). Print sumTo(4). Last line: 10.
Answer
function sumTo(n) {
if (n <= 1) {
return 1;
}
return n + sumTo(n - 1);
}
io.print(sumTo(4));
Lesson 12 — Classes
A class is a named type with fields and methods. new ClassName(...) runs the constructor. Inside a method you can read a field as name; write this.name when a parameter shadows the field (as the constructor does). Chapter: 11.1 A working class. Same file: Examples/Basics/classes_intro.malda.
class Person {
public var name;
public var age;
function Person(name, age) {
this.name = name;
this.age = age;
}
public function introduce() {
io.print("Hi, I'm " + name + " and I'm " + string(age) + " years old.");
}
}
var alice = new Person("Alice", 30);
alice.introduce();
io.print("age=" + string(alice.age));
new Person("Bob", 25) and call introduce(). What is the new last introduce line?
Answer
Hi, I'm Bob and I'm 25 years old.
Box with public var w, constructor Box(w), and public function area() that returns w * w. Print new Box(3).area(). Last line: 9.
Answer
class Box {
public var w;
function Box(w) {
this.w = w;
}
public function area() {
return w * w;
}
}
io.print(new Box(3).area());
Lesson 13 — try / catch
try runs code that may fail. catch handles the thrown value. finally always runs. Chapter: 8.4 Exception handling. Same file: Examples/Basics/try_catch.malda.
try {
throw "something went wrong";
} catch (error) {
io.print("caught: " + error);
}
try {
io.print("in try");
} catch (error) {
io.print("caught: " + error);
} finally {
io.print("cleanup");
}
"disk full". What is the first printed line?
Answer
caught: disk full
throw "something went wrong"; to before the try block. What happens?
Answer
The program stops with an uncaught error. catch only handles throws that run inside its try.
try, throw "nope". In catch, print err= plus the thrown value. Last line: err=nope.
Answer
try {
throw "nope";
} catch (error) {
io.print("err=" + error);
}
Lesson 14 — Prompt as a template
A prompt is a callable template. Without await it does not call a model. Chapter: 10.1 Overview step 1. Same file: Examples/Basics/prompt_template.malda.
prompt greet(name) {
user: "Hello, {name}! How are you today?"
}
var greeting = greet("Alice");
io.print(greeting.user);
greet("MALDA") instead. What prints?
Answer
Hello, MALDA! How are you today?
prompt bye(name) with user: "Goodbye, {name}.". Print bye("Ada").user. Last line: Goodbye, Ada.
Answer
prompt bye(name) {
user: "Goodbye, {name}."
}
io.print(bye("Ada").user);
Lesson 15 — Mini project
A class, a try, and offline schema + validate in one linear program. Catalog note: 35.1. Same file: Examples/Basics/gradebook.malda.
schema Score {
name: string;
points: int;
}
class Grade {
public var name;
public var points;
function Grade(name, points) {
this.name = name;
this.points = points;
}
public function label() {
if (points >= 90) {
return "A";
}
return "B";
}
}
var ada = new Grade("Ada", 92);
io.print(ada.name + ": " + ada.label());
try {
int("not-a-number");
} catch (error) {
io.print("caught bad score");
}
var checked = validate("Score", {
"name": "Ada",
"points": 92
});
if (checked.ok) {
io.print("schema ok: " + checked.data.name);
} else {
io.print("schema failed: " + checked.error);
}
92 to 88. What is the first printed line?
Answer
Ada: B
schema Item { label: string; }. Validate { "label": "pen" }. If it succeeds, print schema ok: plus the label. Last line: schema ok: pen.
Answer
schema Item {
label: string;
}
var checked = validate("Item", {
"label": "pen"
});
if (checked.ok) {
io.print("schema ok: " + checked.data.label);
} else {
io.print("schema failed: " + checked.error);
}
Lesson 16 — Check a mistake
malda check diagnoses a file and does not run it. fn and def are not function keywords — check names them. Chapter: 2.1.2 Diagnose without executing. Same file (the fixed program): Examples/Basics/check_a_mistake.malda.
Paste this broken twin into a scratch file and run malda check scratch.malda. It is not marked runnable here.
fn add(a, b) {
return a + b;
}
var nums = [1, 2, 3];
io.print(nums.length());
Check reports 'fn' is not a function keyword. Use 'function'. Then open the fixed file and run it:
function add(a, b) {
return a + b;
}
var nums = [1, 2, 3];
io.print("sum=" + string(add(2, 3)));
io.print("len=" + string(nums.length));
fn to function, what does malda check still flag on nums.length()?
Answer
.length() should be .length (a property, not a call). malda check --fix can rewrite that one.
function double(n) that returns n * 2. Print double(4) and the length of [1, 2] as len=2. Last line: len=2.
Answer
function double(n) {
return n * 2;
}
io.print(double(4));
io.print("len=" + string([1, 2].length));
Lesson 17 — Two files and a test
include "path"; splices another .malda file at the top level (relative to this file). That is not import / export. Chapter: 1.6 Source composition. Same file: Examples/Basics/modules_include.malda (helper: Examples/Basics/helpers/greet_lib.malda).
include "helpers/greet_lib.malda";
io.print(greet("MALDA"));
io.print("2 + 3 = " + add(2, 3));
io.print("Done.");
Ordinary malda run is the wrong command for tests. Run:
malda test Examples/Testing/unit_test_basics.test.malda
That file uses assert. First section of the test chapter: 36. Property Testing.
function add(a, b) {
return a + b;
}
function factorial(n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
function bound(value, lo, hi) {
if (value < lo) {
return lo;
}
if (value > hi) {
return hi;
}
return value;
}
assert(add(2, 3) == 5, "add(2, 3) should be 5");
assert(factorial(5) == 120, "factorial(5) should be 120");
assert(bound(15, 0, 10) == 10, "bound should cap at hi");
assert(bound(-3, 0, 10) == 0, "bound should floor at lo");
assert(bound(7, 0, 10) == 7, "bound should pass through in-range values");
io.print("unit_test_basics: all asserts passed");
function add(a, b). Add assert(add(10, 5) == 15, "add(10, 5) should be 15"); and print extra assert ok. Last line: extra assert ok.
Answer
function add(a, b) {
return a + b;
}
assert(add(10, 5) == 15, "add(10, 5) should be 15");
io.print("extra assert ok");
Lesson 18 — Offline prompt eval
evalPrompt runs the same validate path as await, with a fixture instead of a model. No API key. Chapter: 10. Prompts after the first-read stop (fixture / evalPrompt, not Mode B/C). Same file: Examples/Basics/prompt_eval.malda. Optional extra: Examples/Prompts/eval_prompt.malda.
schema Card {
name: string;
email: string;
}
prompt extractCard(raw) -> Card {
system: "Extract a contact. JSON only.";
user: raw;
}
var instance = extractCard("Ada Lovelace — ada@example.com");
var ok = evalPrompt(instance, dict { "name": "Ada", "email": "ada@example.com" });
if (ok.ok) {
io.print("card: " + ok.data.name);
} else {
io.print("card fail");
}
var bad = evalPrompt(instance, dict { "email": "nope" });
if (!bad.ok) {
io.print("card expected failure");
}
schema Note { text: string; } and prompt makeNote(raw) -> Note with user: raw. Call evalPrompt(makeNote("x"), dict { "text": "hi" }). If it succeeds, print note: hi. Last line: note: hi.
Answer
schema Note {
text: string;
}
prompt makeNote(raw) -> Note {
user: raw;
}
var checked = evalPrompt(makeNote("x"), dict { "text": "hi" });
if (checked.ok) {
io.print("note: " + checked.data.text);
} else {
io.print("note fail");
}
After the core path
The core course ends at offline prompt eval. Next, pick one track — one file, one first-read box — instead of scanning the sidebar:
- Algorithms —
Examples/Algorithms/towers_of_hanoi.malda. Recursion you already know, applied to a puzzle. Then the rest of the short Algorithms list on Start Here. - Then one hub — stop at the first-read box, then come back:
See Also
- Home — Quick Start and Path 2 (AI apps)
- Start Here — Algorithms and AI theory tracks after Basics
- 2. Tools & Tooling — CLI index
- Glossary — jump to a concept