RFC dan proposal mencatat rancangan. Untuk kemampuan yang benar-benar dapat dipakai, lihat matriks kapabilitas runtime dan catatan rilis.

Introduction to YAZA Programming
From Your First Program to APIs
Author: Yahya Zakariya
Project: YAZA Programming Language
Edition: Living manuscript, 0.6
Status: Coding textbook in progress; implementation snapshot dated 28 October 2026 Audience: Complete beginners, then programmers learning the implemented YAZA language and CLI
Language: English
Source extension: .yz · CLI: yaza
Evidence rule: A feature is taught as working only when repository code and a runnable example or integration check demonstrate it.
Working format: Markdown now; reviewed Word and PDF editions after the manuscript and examples are stable.
Running header for the final paginated edition: Introduction to YAZA Programming · Yahya Zakariya
Running footer for the final paginated edition: YAZA · Living manuscript · 2026
Copyright draft: © 2026 Yahya Zakariya. YAZA source code and third-party components retain their respective licenses. Confirm final publication and license wording before release.
Preface
By Yahya Zakariya
YAZA is a programming-language project created by Yahya Zakariya. This book is a practical introduction to coding with YAZA. It starts with the computer, files, editor, and terminal, then teaches the reader to reason about a problem, write a first program, and gradually build command-line tools, HTTP APIs, and verified database examples.
The course follows a practical loop: understand a small problem, read a complete example, predict what it will do, run it, compare the result, and change one part. It explains programming words before relying on them, uses original YAZA examples, and connects each idea to practice and review questions. Readers do not need previous coding experience. The chapters teach coding and using the language; the separate YAZA architecture book covers compiler and runtime design.
YAZA is still being developed. This manuscript teaches the language and tools that readers can use now, describes their limits plainly, and adds hands-on chapters only after the related code has working evidence. Future plans are not presented as current YAZA features. This preface is written for Yahya Zakariya to review before a final publication edition.
The title and visual design are original to this YAZA manuscript. The reference textbook supplied during planning informed only the broad idea of a carefully ordered introductory course; this book uses its own explanations, examples, exercises, and organization.
Table of Contents
- Preface
- How to use this book
- What this book covers
- Current YAZA tools and verified scope
- Part 0 — Start with a computer and install YAZA
- Part I — Programming foundations
- Part II — Build, organize, and test programs
- Part III — Build and understand HTTP APIs
- Part IV — PostgreSQL and persistent data
- Part V — Security and operations
- Part VI — Expert coding practice
- Curriculum roadmap
- Glossary
- Closing note
- Chapter publication standard
- Manuscript changelog
How to use this book
Each lesson introduces one idea, gives a complete program, explains its important lines, shows the command to run it and the observed output, then offers a short exercise. The answer or reasoning should be included so independent learners can check their understanding. When a feature has a limit, the chapter names it beside the example.
The intended route is to finish Part 0 for the operating system in use, then move through the parts in order. Chapters that depend on a capability still under development are marked as roadmap material and are not presented as usable YAZA code.
What this book covers
This is a coding textbook. It focuses on writing, running, reading, checking, and improving YAZA programs: basic values and control flow, data types, functions, modules, files, tests, command-line applications, APIs, and PostgreSQL examples supported by the repository.
The first two parts teach pure YAZA: programs written with the language and its verified standard-library/runtime functions, before introducing the HTTP API framework. Readers learn values, control flow, functions, shapes, variants, Option, Result, modules, command-line work, files, and tests on their own. The later API and database parts build on those foundations; they do not replace the language lessons.
It is separate from the YAZA architecture book, which explains compiler/runtime design, system boundaries, proposals, and comparisons with other language platforms. This textbook will mention implementation details only when they help a learner understand a program or avoid relying on a capability that is not implemented.
Current YAZA tools and verified scope
- YAZA source files use
.yz; the CLI isyaza. - The self-hosted compiler checks the supported language subset, emits C, and invokes the host C compiler for native programs. A standalone machine-code backend and bytecode VM are not part of the current workflow.
- Runnable examples cover variables,
Text, integer and floating-point values, conditions, loops, functions,shape, tagged variants,Option,Result, local modules, files/bytes, processes, and tests. - The API starter has an HTTP/1 runtime, typed input examples, request validation, generated Postman/OpenAPI artifacts, and executable YAZA controller/service/repository functions. Product CRUD uses the YAZA PostgreSQL adapter when
DATABASE_URLis set; otherwisestd/api/memory_records.yzcalls a bounded native in-memory table adapter. Both paths return repository results through the ordinary handler response writer. HTTP response construction and the in-memory table itself remain native runtime code; database startup/schema/readiness use the pure-YAZA PostgreSQL client. - Database-configured HTTP smokes have verified migration, Product CRUD, persistence, readiness recovery, and safe database-error responses against disposable PostgreSQL 17.6 and 18.6 services. Remote TLS interoperability, a wider server-version matrix, and hosted live-database CI remain unverified.
- The HTTP server uses one internal scheduler task per connection to read requests through the readiness bridge. It dispatches generated controller handlers and database-backed readiness probes to a bounded worker pool; the event loop owns socket reads/writes and response delivery. PostgreSQL operations remain synchronous within workers. Scheduler tasks and
async/awaitare not available in YAZA source syntax. - No production Linux/Windows release, browser/WebAssembly target, mobile compiler, YAZA UI renderer, statistics/dataframe engine, or additional database provider is taught as available.
For changing details, consult the runtime capability matrix, Appendix G implementation status, Appendix H security status, and API completion plan.
Part 0 — Start with a computer and install YAZA
A few words before the first program
Coding means writing instructions that a computer can follow. A computer does not guess what a person meant: the instructions must follow the language's rules. Learning to program is therefore a mix of describing a solution clearly, writing that description in code, and checking the result.
This book assumes that the reader can use a keyboard and open a folder. It does not assume that the reader knows what a compiler, variable, function, or API is. New words are explained before they become part of an exercise. If a command behaves differently on a reader's machine, the first thing to check is the exact YAZA version and platform support listed in this section.
Files, folders, and the terminal
A file stores information under a name. A YAZA source file ends in .yz, for example hello.yz. A folder contains files and other folders. A path tells the computer where a file is, such as examples/native_compiler_hello.yz. A path can be relative to the folder where a command is run or absolute from the root of the computer's file system.
The terminal is a text-based way to give commands to the computer. A shell reads each command and starts the requested program. The command line is not YAZA code: it is how the reader asks the operating system to start the YAZA CLI. The CLI is a separate program named yaza that can check, build, run, or test YAZA files.
From a terminal, these commands show and change the current folder on macOS:
pwd
ls
cd path/to/a/folderpwd prints the current folder, ls lists its contents, and cd changes the current folder. A relative path such as examples/native_compiler_hello.yz works only when the terminal starts in the project folder that contains examples. If YAZA reports that it cannot open a file, check pwd, confirm the file appears in ls, and then retry from the correct folder.
An editor is a program used to create and change source files. Save code as plain text with the .yz extension. Do not add a second extension such as .txt, and do not paste terminal commands into a .yz file. The VS Code YAZA extension improves highlighting, but the compiler remains the authority on whether code is valid.
Install and check YAZA
An installer places the program files where the operating system can find them and may add the command to PATH. A YAZA installation guide must match a published build for the reader's operating system. The platform table below records which guides can be written as verified, copy-and-paste instructions today.
| Platform | Evidence-backed manuscript status |
|---|---|
| macOS Apple Silicon | A native CLI bootstrap and package build exist. The package is unsigned and still needs a clean-machine installation review; installation instructions will be labeled as a developer preview until that check passes. |
| Linux | Runtime source includes Linux paths, but a complete CLI build, installer, and clean-system install test are not established. No copy-and-paste install command will be presented as working until those checks pass. |
| Windows | A Windows CLI build, installer, and clean-system install test are not established. The Windows guide remains a release gate, not an executable tutorial today. |
The intended course begins with those three platform guides, then teaches programming concepts only after the reader can verify their own installation. Linux or Windows instructions will not be inferred from macOS commands.
After installing a supported build, check that the shell can find it:
yaza --version
yaza helpIf the command is unavailable, the shell has not found the executable. This is usually an installation or PATH issue, not an error in the reader's YAZA source. PATH is a list of folders the shell searches for commands. The exact installer and recovery steps depend on the operating system and release channel, so follow only the guide for the build that has actually been published and checked.
This book describes the implementation that exists today. “YAZA aims to…” describes a goal, not a claim that the goal is already delivered.
Curriculum roadmap
This is the planned progression for the coding textbook, modeled on a conventional introductory programming course: concepts accumulate from small programs to complete applications. A roadmap title is not evidence that its chapter is already written or that an unfinished YAZA feature works.
Part I — Programming foundations
- What a program is; source code, compiler, and running program
- Your first YAZA program and the
.yzfile - Variables, values, and types
- Text, numbers, expressions, and checked conversions
- Conditions with
if,elif, andelse - Repetition with
while, text lists, numeric ranges,break, andcontinue - Functions, parameters, return values, and local state
shapevalues and checked fields- Tagged variants,
match,Option, andResult
Part II — Build, organize, and test programs
- Text, lists, bytes, and bounded data
- Local modules and imports
- Command-line arguments and simple command-line programs
- Standard input, files, directories, and binary data
- Child processes, timeouts, and captured output
- Assertions, test files, and reading test results
- Project manifests and the
yazabuild/run/check workflow - Compiler diagnostics, build modes, and architecture checks where supported
Part III — Build and understand HTTP APIs
- Requests, routes, handlers, status codes, and responses
- Generate and run an API project
- Path, query, header, and JSON-body inputs
- Typed shapes, optional fields, and request validation
- Controller, service, repository, and interface metadata
- Safe handler results and error responses
- Postman and OpenAPI generation
- Health, readiness, logs, metrics, authentication, CORS, and rate limits
- API smoke checks and diagnosing a failing request
Part IV — PostgreSQL and persistent data
- Why programs store data in databases
- Configure and run the verified local PostgreSQL example
- Bound parameters and CRUD from the YAZA repository path
- Migrations, migration status, and schema snapshots
- Transactions and savepoints
- Timeouts, credentials, TLS policy, and database error handling
- What adding another database provider requires
Part V — Security and operations
- Validate data at network and file boundaries
- Authentication, CORS, rate limiting, and safe logs
yaza audit --security: useful findings and blind spots- Resource bounds, sanitizers, fuzzing, and safe shutdown
Part VI — Expert coding practice
- Read generated C and understand the native build boundary
- Organize modules and enforce project layer rules
- Measure a program without inventing performance claims
- Build a complete command-line capstone
- Build an API/PostgreSQL capstone and document its tested limits
Chapters will be drafted and expanded while the corresponding YAZA implementation progresses. Topics such as general classes/inheritance, browser UI, mobile rendering, general async handlers, extra database providers, and a dataframe/statistics engine stay outside the hands-on curriculum until working implementations and evidence exist.
Chapter teaching standard
Every full chapter is planned to include:
- Learning goals that say what the reader should be able to do.
- Key ideas that explain new terms in plain language before using them.
- A problem-driven example that gives the new syntax a reason to exist.
- A complete runnable YAZA program, followed by a line-by-line walkthrough and its observed output.
- Checkpoints that let a learner predict or explain a result before moving on.
- Common mistakes with the compiler/runtime message and a correction.
- Practice problems, ordered from small edits to a short independent program, with solutions or reasoning.
- A chapter summary and an explicit note about the supported language/runtime boundary.
API and database lessons must trace a real request or query, state prerequisites, and identify native-runtime boundaries where relevant. The current manuscript contains only some lessons; this roadmap is not presented as a finished book.
Part I — Programming foundations
Before beginning, finish the installation guide for the reader's operating system and verify the CLI version. The repository's currently verified local path is ./dist/yaza; use an installed yaza command only after the corresponding installer has been tested on that platform.
Chapter 1 — What is a program?
A program is a set of instructions a computer follows. A problem describes what needs to happen; an algorithm is a precise sequence of steps that solves it; source code is that solution written in a programming language. A compiler checks source code and translates it into a form that can run. The runtime provides operations a running program relies on, such as writing text to the terminal.
For example, to double a number, first decide what number to use, multiply it by two, and display the result. Written as simple planning steps, the algorithm is: store 21, calculate 21 times 2, display 42. The runnable YAZA examples in the next chapters show how to express calculations, decisions, and repetition in the language. The important habit is to break a large task into steps small enough to explain and inspect before writing the full program.
When a compiler reports an error, it is saying that the source did not pass a rule it checks. Read the first diagnostic carefully, find the reported file and line, make one correction, then run the command again. A compiler error is ordinary feedback during programming; it does not mean the computer or the project is damaged.
From the project root, run a single file with:
yaza run examples/native_compiler_hello.yzWhen using the local repository build, the command is ./dist/yaza run examples/native_compiler_hello.yz.
The command contains three parts: yaza starts the CLI, run asks it to compile and execute a program, and the final path names the source file. The terminal's current folder matters because this example uses a relative path. The shell and YAZA source are two different interfaces: type the command at the terminal prompt, and type YAZA statements inside the .yz file.
Try it: Before running the example, read its source and write down what you expect each line to do. Run it, then compare the output with your prediction. If it differs, check whether the file was saved and whether the CLI is running the file you edited.
Chapter 2 — Your first YAZA program
Open examples/native_compiler_hello.yz:
start:
let language: Text = "YAZA"
let year: Number = 2026
say "Hello from {language}, built in {year}."
say language
say year
Run it with yaza run examples/native_compiler_hello.yz. After the runner's startup summary, the program writes:
Hello from YAZA, built in 2026.
YAZA
2026
start: is the program entry point. Indentation groups instructions under it. let creates a variable; language and year are names; Text and Number are types; = gives each variable its first value. say writes to the terminal, and braces inside the text insert the current value of a variable.
Read the program from top to bottom. First YAZA stores the text YAZA under the name language. It stores the whole number 2026 under year. The first say creates a sentence using both stored values. The next two lines print each value separately. The output has three lines because the program contains three say instructions.
The symbol : introduces the indented body of start. The statements inside that body must stay indented consistently. The type after a variable name tells the compiler what kind of value is expected. A Text value is written between double quotation marks; a Number in this example is a whole number. The compiler can report a type error before the program runs if the assigned value does not fit its declared type.
The current compiler emits C and invokes a host C compiler to make a native executable. The source code the reader writes is still YAZA.
Common mistakes: forgetting start:, placing a statement outside its indented block, leaving off a closing quotation mark, or saving the file with the wrong extension. Fix one issue at a time and use the first diagnostic as a starting point.
Practice: Change the greeting to include your own name in a new Text variable, then print one sentence with both the language name and your name. Keep the text inside quotation marks and use the variable name between braces.
Check your result: the program should still have a start: block, should compile, and should print one line containing the chosen name. The exact name is up to the reader.
Chapter 3 — Variables and types
A variable is a named place to keep a value while a program runs. language stores text; year stores a whole number. YAZA checks that the assigned value matches the declared type.
| Type | What it stores | Example |
|---|---|---|
Text |
Text characters | "YAZA" |
Number |
Signed 64-bit whole number | 2026 |
Int32 |
Checked signed 32-bit whole number | API examples |
Float64 |
Decimal/floating-point number | 1.25 |
Boolean |
true or false |
available: true |
TextList |
A bounded list of text values | Loop example |
List[T] |
A typed list of a supported YAZA value, such as Number or a shape |
Chapter 10 |
Use let when a value will not be reassigned. Use var when it must change, and set to assign a new value. examples/functions.yz demonstrates it:
fn twice(value: Number) -> Number:
var result: Number = value
set result = result + value
return result
The declared type is part of the variable's contract. A name makes a value easier to reuse and explain; a type lets the compiler reject many mismatches before execution. Prefer a clear name such as item_count over a vague name such as x when the meaning would otherwise be hard to understand.
let marks a value that the program will not replace. var marks a value that may be reassigned. set makes that change visible in the code. A value's type does not change when it is reassigned: a Number variable must continue to receive a Number value.
Practice: In the twice function, change the local variable name result to total. Update every use of the name consistently. Then run the example and confirm the result stays the same.
Check your result: a name is only changed when the declaration and all references use the new spelling. If one reference is left behind, the compiler should report that it cannot resolve the old name.
Chapter 4 — Numbers and expressions
An expression combines values to calculate a result. The function above uses result + value. YAZA checks integer overflow and invalid division in the documented integer types. Number is signed 64-bit; decimal and scientific literals can use Float64. Conversions are explicit rather than silently changing a value to another numeric type.
Exercise: in examples/functions.yz, change the argument passed to twice from 21 to another number. Predict the result before running the file.
An operator combines or changes values. + adds numbers, - subtracts, * multiplies, and / divides when the operand types and runtime rules allow it. Parentheses group work so that a reader can see which calculation happens first. A Boolean expression produces true or false and is useful when choosing a branch or deciding whether a loop should continue.
Use a paper calculation or a small program to check boundary cases: zero, a negative number, and a value near the type's limit. Integer arithmetic is exact only while the answer fits its type. Floating-point values such as Float64 can represent many fractions efficiently, but some decimal fractions are approximations in binary; avoid assuming that every displayed decimal is mathematically exact.
Check your result: twice(21) returns 42. If you change the argument to 8, it returns 16. The input is doubled because the function adds the same value to itself.
Chapter 5 — Decisions
Programs often choose between actions. An if statement checks a condition; elif checks another condition only when earlier branches did not match; else handles the remaining case. Conditions here compare values and produce a Boolean result.
The runnable examples/elif.yz classifies numbers:
fn classify(value: Number) -> Text:
var label: Text = "very positive"
if value < 0:
if value < -10:
set label = "very negative"
else:
set label = "negative"
elif value == 0:
set label = "zero"
elif value <= 10:
set label = "positive"
else:
set label = "very positive"
return label
start:
say classify(-12)
say classify(-3)
say classify(0)
say classify(8)
say classify(12)
The first comparison asks whether the value is below zero. If so, a nested if separates values below -10 from the other negative values. Otherwise, the elif branches check zero and then values up to 10. If none of those branches match, else runs. Branch order matters: after a branch runs, the rest of that if/elif chain is skipped.
YAZA accepts one-line if, elif, and else branches when each branch contains one statement. They can form a single chain, which is useful for early returns:
fn classify(value: Number) -> Text:
if value < -10: return "very negative"
elif value < 0: return "negative"
elif value == 0: return "zero"
elif value <= 10: return "positive"
else: return "very positive"
The first matching branch exits the function immediately. For a version that stores the answer first, declare a mutable variable with var, update it with set, and return it after the chain:
fn classify_with_label(value: Number) -> Text:
var label: Text = "very positive"
if value < -10: set label = "very negative"
elif value < 0: set label = "negative"
elif value == 0: set label = "zero"
elif value <= 10: set label = "positive"
else: set label = "very positive"
return label
The two styles are both supported and tested. set label = ... updates a mutable variable; it is different from a labeled break or continue, which YAZA does not currently support. The runnable examples/elif.yz demonstrates the block-form var/set approach.
For numeric classification, YAZA also supports an exhaustive guarded return match form. It evaluates the matched value once, checks arms from top to bottom, and requires the final else arm:
fn classify(value: Number) -> Text:
return match value:
< -10 -> "very negative"
< 0 -> "negative"
== 0 -> "zero"
<= 10 -> "positive"
else -> "very positive"
The runnable examples/numeric_match.yz shows its output. Guarded numeric matches currently accept Number, Int32, and Float64, and every arm must return a value compatible with the function's declared return type. For tagged variants, Option, and Result, exhaustive match arms can also produce a value in a return or assignment context:
fn describe(value: Option[Text]) -> Text:
return match value:
some(text) -> "value: " + text
none -> "empty"
fn describe_into_label(value: Option[Text]) -> Text:
var label: Text = "not assigned"
set label = match value:
some(text) -> "value: " + text
none -> "empty"
return label
fn describe_with_let(value: Option[Text]) -> Text:
let label: Text = match value:
some(text) -> "value: " + text
none -> "empty"
return label
set updates a mutable variable declared earlier with var; it does not create a new variable. A typed let or var can also be initialized by an exhaustive match, and the new variable becomes available after the match finishes. The runnable examples/match-forms.yz puts conditional assignment, early returns, numeric matches, sum-type matches, and a match initializer together. Match is supported in direct return, typed let/var initialization, and set contexts; a match nested inside a call/calculation or passed directly to say is not supported yet.
If a function should keep a mutable result variable, the same guarded match can assign into it. Every arm must produce a value compatible with the variable's declared type, and else ensures that every path assigns a result:
fn classify_into_label(value: Number) -> Text:
var label: Text = "not classified"
set label = match value:
< -10 -> "very negative"
< 0 -> "negative"
== 0 -> "zero"
<= 10 -> "positive"
else -> "very positive"
return label
This form is for an existing var; set cannot modify a let value. Numeric and sum-type matches support function returns, typed let/var initialization, and set assignment, but not arbitrary nested expressions.
The output is:
very negative
negative
zero
positive
very positive
Practice: Change the threshold so that 10 is labeled positive, while 11 is labeled very positive. Test both boundary values. Check: use <= 10 for the positive branch and let the final else handle values greater than 10.
Chapter 6 — Repetition
Loops repeat instructions. A while loop repeats as long as its condition is true; the code inside the loop must eventually change a value that can make the condition false. Otherwise, the program may loop forever. examples/functions.yz uses while to count down toward zero.
A for loop visits each value in a collection or range. The range 1..6 starts at 1 and stops before 6, so its values are 1, 2, 3, 4, and 5. The runnable examples/for_loop.yz includes this summation:
fn sum_range(start: Number, stop: Number) -> Number:
var total: Number = 0
for value in start..stop:
set total = total + value
return total
start:
say number_text(sum_range(1, 6))
total begins at zero. Each turn adds the current value. When the range is finished, the function returns 15. The same example loops over a TextList; continue skips the rest of the current turn, and break leaves the loop early. Use those controls only when they make the loop's purpose clearer.
Practice: Change the range to 1..4. Check: the loop visits 1, 2, and 3, and returns 6. Remember that the stop value is excluded.
Chapter 7 — Functions
A function gives a task a reusable name. It declares the types of its inputs and output:
fn add(left: Number, right: Number) -> Number:
return left + right
left and right are parameters. Number after -> is the return type. A call supplies arguments for the parameters. The compiler checks their types. This form is demonstrated by the runnable functions example.
Think of a function as a named calculation with declared inputs and one declared result. The call add(4, 5) supplies the values 4 and 5; inside the function, those values are available under the parameter names left and right. return sends the computed value back to the caller. A function can then be used anywhere an expression of its return type is expected.
The examples/functions.yz file also demonstrates a function that repeats an operation and a function that uses a loop. Read the function declaration before its call: the declaration tells you what arguments are required, and the call shows what the program supplies.
Practice: Add a call to add(12, 30) and print the returned number with number_text. Check: the result is 42. If the compiler reports an argument or return type mismatch, compare each supplied argument and returned value with the function's declared types.
Chapter 8 — Shapes
A shape groups related named fields into one value. It is the demonstrated record-like data type; it is not a class with inheritance or automatic database mapping.
examples/shapes.yz defines a Product with an id, name, price, and availability, creates a value, passes it to a function, and returns a changed value. Run it and compare the field order in the declaration with the constructor call.
The declaration gives the shape's name and the type of every field. The constructor in the example follows the declared field order:
shape Product:
id: Number
name: Text
price: Int32
available: Boolean
Product(7, "YAZA Language Guide", int32_from_number(1499), true) creates one value with four fields. The rename function receives that value and returns another Product with a new name. This is a typed way to group related data; the current feature is not a class system, inheritance mechanism, or automatic database mapper.
Practice: Change the sample product's name and run the example. Check: the printed product name changes, while its id, price, and availability keep their declared types.
Chapter 9 — Variants and values that may be absent or fail
A tagged variant lets a program represent one of several named cases; match handles those cases explicitly. Option[T] represents a value that may be absent, and Result[T, E] represents success or failure. For example, Option[Number] contains a number or none; Result[Number, Text] contains ok(value) or err(message).
examples/variants.yz, examples/match.yz, examples/option.yz, and examples/result.yz show the supported forms. The type context must identify the intended Option or Result; the broader generic trait and error ecosystem is incomplete, so this lesson teaches only the demonstrated forms.
Use Option[T] when a value might not exist. For example, a search can return some(42) when it finds a value and none when it does not. Use Result[T, E] when a calculation can succeed or fail: ok(value) carries the success value, while err(message) carries the error value. The caller should inspect the case before unwrapping its payload.
examples/match.yz handles each case with match:
fn result_label(value: Result[Number, Text]) -> Text:
match value:
ok(number):
return number_text(number)
err(message):
return message
The ok branch binds its contained number to number; the err branch binds its message to message. The compiler checks that the cases required by this supported variant are handled. This gives a reader a visible place to decide what each outcome means instead of treating a missing value as if it were present.
Propagating errors with the question-mark operator
When a function already returns a compatible Result, postfix ? can pass an error back to its caller immediately. If the operation succeeds, the value inside ok(...) is available to the next statement. The complete runnable example is examples/result_propagation.yz:
fn positive_number(value: Number) -> Result[Number, Text]:
if value > 0:
return ok(value)
return err("value must be positive")
fn doubled_positive(value: Number) -> Result[Number, Text]:
let accepted: Number = positive_number(value)?
return ok(accepted * 2)
fn forwarded(value: Number) -> Result[Number, Text]:
return positive_number(value)?
start:
let doubled: Result[Number, Text] = doubled_positive(4)
let rejected: Result[Number, Text] = doubled_positive(-1)
say result_unwrap(doubled)
say result_unwrap_error(rejected)
say result_unwrap(forwarded(9))
The first call succeeds, so doubled_positive(4) prints 8. The negative input produces err("value must be positive"); ? returns that error from doubled_positive without running the multiplication. The final call forwards a successful result and prints 9. YAZA also checks ? in a var initializer, a set statement, and a direct return. The supported error type must match the enclosing function's error type, or have one unambiguous, explicitly declared direct From[Source] for Target conversion. Chained conversions, generic trait resolution, and ? inside arbitrary nested expressions are not supported.
The output is:
8
value must be positive
9
Practice: Change the 4 passed to doubled_positive to -3. Predict the three output lines before running yaza run examples/result_propagation.yz. Check: the first line becomes value must be positive; the error passes through, so no doubled number is produced for -3.
Current chapter status
- Part I: Beginner lessons cover implemented pure-YAZA syntax, including early returns, conditional assignment, numeric and sum-type matches, compatible
Resultpropagation with?, and safe typed-list access; examples and exercises will continue to receive chapter-by-chapter review. - Part II: Chapters 10–13 now contain beginner explanations and practice prompts tied to existing text, bytes, modules, CLI, and file examples. Dedicated testing lessons, complete answer keys, and clean-checkout example review remain to be written.
- Parts III–IV: API and PostgreSQL behavior has concrete runnable and live-smoke evidence, but the material is currently a compact learning path rather than a finished textbook treatment.
- Parts V–VI: Current security and CLI practice are summarized with limits; the full lesson sequence and capstone walkthroughs remain to be written.
The manuscript is a living course, not a claim that all 42 roadmap chapters are finished.
Part II — Build, organize, and test programs
Chapter 10 — Text, lists, and bytes
Learning goals: split and join text, store values in TextList and List[T], distinguish readable text from arbitrary bytes, and handle indexes that may be out of range.
Text is useful for names, messages, and file formats that are meant to be read as text. A TextList stores an ordered sequence of text values. The following small program uses comma-separated names:
fn format_names(names: TextList) -> Text:
return text_join(names, " | ")
start:
let names: TextList = text_split("Ada,Grace,Linus", ",")
say "Names: {list_count(names)}"
say "First: {list_get(names, 0)}"
say format_names(names)
text_split returns three list items. list_count reports how many there are; list_get reads a zero-based position, so the first position is 0. text_join combines the items with the separator supplied by the program. The output is:
Names: 3
First: Ada
Ada | Grace | Linus
TextList is the dedicated text collection. YAZA also supports a concrete typed collection form, List[T], for supported built-in, shape, variant, Option, and Result values. For example, tests/typed_list.test.yz exercises typed product values, numbers, nested options, and text lists. This is a built-in collection feature, not general user-defined generic functions or traits.
list_get(items, index) reads a zero-based position and terminates with a runtime error if the index is invalid. When an index may be outside the list, use list_try_get(items, index): it returns Option[T], with some(value) for a present element and none for a negative or out-of-range index. Match the result so both outcomes are handled:
let first_score: Option[Number] = list_try_get(scores, 0)
match first_score:
some(score):
say "First score: {score}"
none:
say "There is no score at that position"
The complete runnable example is examples/list_safe_access.yz; run it with yaza run examples/list_safe_access.yz. List[T] currently provides count, append, indexed read/write, safe indexed read, and for item in values iteration. A collection loop captures the list length when it starts, so values appended during that loop are not visited until a later loop. Slicing and a complete collection library are still under development.
Text operations such as text_length, text_slice, and text_byte currently use byte positions. A UTF-8 character can occupy more than one byte, so a byte count is not always the number of user-visible characters. Do not cut text at an arbitrary byte boundary when processing user-facing Unicode text.
Binary data is different. It may contain any byte value, including zero, and must not be treated as a zero-terminated text string. YAZA's Bytes type carries a length with its data. The runnable examples/cli_bytes.yz demonstrates creating bytes from text, concatenating a suffix, writing and reading a binary file, reading a byte range, checking equality, and inspecting a byte value:
let source: Bytes = bytes_from_text("YAZA")
let suffix: Bytes = bytes_from_text("!")
let content: Bytes = bytes_concat(source, suffix)
The full example writes and reads content, then checks that the loaded bytes match. bytes_length returns the byte count and bytes_get returns a number from 0 through 255. Current runtime operations limit one Bytes value to 64 MiB. Text-to-Bytes conversion is for text content; use byte constructors or file reads for data that is not text.
Checkpoint: What is the first list index? Why should an image file be read as Bytes rather than Text?
Practice: Split "red;green;blue" on ";", print the number of colors, and join them with ", ". The expected list count is 3; the joined text is red, green, blue.
Chapter 11 — Modules and imports
A module lets a program keep reusable declarations in another source file. In examples/modules/main.yz, the first line imports the local module named math:
use "math"
start:
let total: Number = sum_to(10)
say "Sum from 1 to 10: {total}"
The companion examples/modules/math.yz defines the imported function:
fn sum_to(value: Number) -> Number:
var total: Number = 0
var current: Number = value
while current > 0:
set total = total + current
set current = current - 1
return total
The project keeps these files together in a directory. The import path omits the .yz extension, and the compiler resolves the module relative to the importing source file. The module's typed function declaration supplies the reusable operation; the caller passes 10 and receives 55.
YAZA also ships standard-library source modules. For example, use "std/cache/ttl" imports a bounded, process-local text cache. Its functions are ttl_cache_get(key), ttl_cache_set(key, value, ttl_ms), ttl_cache_remove(key), and ttl_cache_clear(). The cache is volatile and limited to 128 entries, keys of 255 bytes, values of 4095 bytes, and TTLs up to seven days; do not use it for secrets or authorization decisions. The compiler/runtime tests exercise these limits and expiration behavior.
This is local source-module support. It does not install third-party packages, publish modules to a registry, or define a complete package ecosystem. For a first exercise, change the call to sum_to(5) and work out the returned total before running the example.
Chapter 12 — Command-line programs
A command-line program reads arguments that follow its name. examples/native_compiler_arguments.yz demonstrates the core operations:
start:
let count: Number = arg_count()
if count > 0:
let first: Text = arg(1)
say "Argument count {count}; first argument: {first}"
else:
say "No program arguments"
Run it with a value after -- so the YAZA CLI knows the value is for the program:
yaza run examples/native_compiler_arguments.yz -- AdaThe program sees one argument and reads it with arg(1). The first user argument is position 1; position 0 is reserved for the executable name in the runtime. Calling arg for a position that was not supplied reports an error, so check arg_count() first. examples/cli_dispatch.yz builds on this idea to choose a command and print usage for missing input.
Practice: change the program so it prints a usage message when there is no argument and greets the first argument when one is present. Try it once with -- Ada and once without any program argument.
Chapter 13 — Files and binary data
Programs can read and write files using runtime functions. examples/compiler_primitives.yz shows text file reads and writes, path joining, directory listing, line splitting, and a child process. examples/cli_append.yz shows appending text without replacing existing file content. examples/cli_stdin.yz reads text from standard input.
Child-process functions pass arguments as a list instead of building a shell command string. process and the older timeout/capture forms inherit the parent environment. For a smaller child environment, use process_env(command, arguments, environment) or process_timeout_env(command, arguments, environment, timeout_ms). Each environment entry has the form NAME=value; the child receives only those entries, loader-injection variables are rejected, and an explicitly supplied PATH must contain absolute directories without . or .. segments. The runnable examples/cli_process_environment.yz demonstrates this form. It reduces accidental environment leakage, but it is not an OS sandbox and does not make an untrusted executable safe.
The return value from file_write and file_append is a status number: 0 indicates success in the examples, and a nonzero value indicates failure. file_read reports a runtime error if the file cannot be opened. Use path_join to combine path components rather than manually inserting separators. Resolve and validate paths derived from untrusted input before accessing them; the existence of a file API does not make a path safe.
For binary content, use file_read_bytes, file_write_bytes, and Bytes, as demonstrated in Chapter 10. A whole-file Bytes read is bounded at 64 MiB. file_read_bytes_range can read a bounded range from a file. stdin_read and stdin_read_bytes are each bounded at 1 MiB. These runtime limits are important when processing untrusted or unexpectedly large files.
Practice: adapt the binary example to write a short value, read it back, compare both values with bytes_equal, then remove the temporary file with file_remove. Keep the path inside a directory you control.
Tests
YAZA test files use .test.yz or _test.yz and can use assert for Boolean conditions. The CLI discovers tests in sorted order, builds and runs them with a timeout, and reports failures. Test files are executable checks, not proofs that every possible input is correct. Use existing repository test files as examples, then change a working example and rerun the relevant check before writing new tests.
Command-line workflow
yaza help
yaza run examples/native_compiler_hello.yz
yaza check examples/native_compiler_hello.yz
yaza build examples/native_compiler_hello.yz -o hello
yaza testThe single-file build produces an executable using the host C toolchain. Project commands use yaza.toml and the project entry point. Options and supported targets can change, so check yaza help from the installed version.
Part III — Build and understand HTTP APIs
What an API does
An HTTP API receives a request, finds a matching method and path, runs application code, and sends a response with a status code and body. GET /api/greeting asks for greeting data; POST /api/typed-body sends JSON to the server.
Generate and run the starter:
yaza new --api hello-api
cd hello-api
yaza check src/main.yz
yaza runBy default, the starter listens on 127.0.0.1:3000. Its generated README is the source for exact routes and settings; see the API template guide.
Follow a request through layers
The executable greeting example provides this real path:
HTTP route
→ GreetingControllerImpl
→ GreetingServiceImpl
→ GreetingRepositoryImpl
→ JSON response
These greeting functions are executable YAZA code. The API smoke script requests the route and checks its response. The template also demonstrates typed path/query/header reads, scalar JSON bodies, and nested request shapes.
Route handlers can return Text, Option[Text], declared shape values, supported lists of shapes, or compatible Result values. none becomes a safe 404 Not Found response. When E is the template's ApiError, every declared variant maps to a stable status and code. Payload-bearing cases include InvalidInput(field) and ValidationFailed(errors): their values are returned only when each value passes the bounded safe-path rule for dotted identifiers and canonical array indexes from 0 through 63; unsafe text is omitted. Unknown cases and other error types use a generic 500 without exposing their payloads. A 401 includes WWW-Authenticate: Bearer; 429 and 503 include Retry-After: 1. The HTTP smoke checks the mappings and verifies that internal or unsafe error text is not leaked. Typed conversion for framework validation, authentication, and database failures is not implemented yet.
The API template demonstrates the checked behavior here:
curl -i 'http://127.0.0.1:3000/api/result-greeting/Ada'
curl -i 'http://127.0.0.1:3000/api/result-greeting/error'
curl -i 'http://127.0.0.1:3000/api/typed-result-greeting/invalid'
curl -i 'http://127.0.0.1:3000/api/typed-result-greeting/unsafe-field'The first typed error returns a 422 body with the safe field path profile.email. The second returns the same status and error code but omits its unsafe field text. A service can construct this error with err(ApiError::InvalidInput("profile.email")).
The Product CRUD route is useful for learning HTTP verbs, validation, status codes, Postman, and database configuration. All six routes call explicit YAZA controller → service → repository functions with compiler-checked layer bindings. With DATABASE_URL, repository functions use the YAZA PostgreSQL adapter for list, find, create, replace, partial update, and delete; a bounded process-local connection pool is used. Without it, the demo uses a bounded native in-memory store. Product interface files still provide compile-time metadata rather than runtime-polymorphic dispatch, and response construction plus some lifecycle work still cross into native runtime code. Database-configured API smokes have verified CRUD, restart persistence, readiness recovery, and safe database-error responses against disposable PostgreSQL 17.6 and 18.6 services. This is a verified integration slice, not completion of the API release gates; see the API completion plan.
Inputs, validation, and generated contracts
The API template demonstrates path parameters, query parameters, headers, typed Number/Int32/Float64/Boolean values, scalar JSON properties, nested shape request bodies, optional fields, and declared numeric/text/list-size rules. Text fields also support a small anchored ASCII pattern subset. For example, validate handle pattern "^[A-Za-z][A-Za-z0-9_-]*$" permits a letter first, then letters, digits, underscores, or hyphens. Patterns allow literals, ., character classes/ranges/negation, escaped punctuation, and ?, *, +; they are limited to 128 bytes and 64 atoms, while input Text is limited to 1023 UTF-8 bytes. Matching has bounded dynamic-programming cost and does not use backtracking. Groups, alternation, shorthand classes, backreferences, Unicode patterns, and bounded quantifiers are unsupported. OpenAPI includes the same pattern, and a mismatch returns the fixed pattern validation code. Runtime validation rejects malformed or out-of-range values before handler dispatch. Variants and general maps are not supported by the request decoder.
In an API project, run:
yaza generateThe command writes Postman, OpenAPI 3.1, and Swagger-compatible files together under docs/api/; --postman, --openapi, and --swagger select a single format. The Postman collection includes absolute request URLs and declared path/query/header inputs. For scalar request bodies, the CLI synthesizes a neutral sample from declared field types; example json {...} customizes generated documentation only. OpenAPI derives supported request and response schemas, common error responses, and route authentication metadata from the current declarations. General contract versioning and full typed framework/authentication/database error conversion remain incomplete.
Part IV — PostgreSQL and persistent data
PostgreSQL and persistence
The current API template includes a PostgreSQL demo. When DATABASE_URL is set, Product CRUD, startup/schema checks, and readiness call YAZA-authored PostgreSQL v3 modules with parameterized JSONB operations; generated API binaries do not link libpq. The project includes ordered, checksum-verified migrations, migration status, schema snapshots, and transaction/savepoint helpers. The bounded process-local pool has a default size of eight connections per database identity. The included local Docker example explicitly uses sslmode=disable with 127.0.0.1; remote connections require verified TLS with sslmode=verify-full, and weaker fallback modes are rejected. Database-configured HTTP smokes have verified migration, CRUD, persistence after restart, readiness recovery, and safe database-error responses against disposable PostgreSQL 17.6 and 18.6 services. Remote TLS interoperability, broader server versions, and hosted live-database CI remain unverified.
Teach the verified local setup with its prerequisites and boundaries visible. Do not imply that every database operation or security profile has been validated. See the database driver proposal for the planned provider-neutral database work.
YAZA does not yet ship another database provider. PostgreSQL is the only current API database demo. Additional provider chapters wait until each engine and its tests are implemented and verified.
Part V — Security and operations
The API starter supports a shared Bearer token, exact-origin CORS configuration, health/readiness endpoints, request metrics, structured access logs, JSON validation, bounded request handling, graceful signal handling, and optional per-client request limiting. Loopback development may omit the token; a non-loopback IPv4 bind requires YAZA_API_TOKEN or JWT-only mode with at least one auth jwt route and explicit auth policies for every declared route. Token comparison is constant-time for equal-length values, and duplicate Authorization headers fail closed. The optional IPv4 token bucket is process-local and has a bounded address table; it is not a distributed quota service or route-specific policy. Route-scoped JWT HS256 verification is available with a configured secret, issuer, audience, and expiration. A handler on a matched JWT route can read bounded verified string claims with http_auth_claim_text(name) -> Option[Text]; public and shared-Bearer handlers cannot get claims from headers, and the temporary payload is wiped after dispatch. YAZA does not issue tokens or provide typed principals and centralized role/ownership authorization. A non-loopback JWT-only deployment requires at least one JWT route and an explicit auth policy on every route. Inbound HTTP TLS is not part of the current server; deployment needs a documented trusted TLS boundary.
yaza audit --security scans selected source/configuration patterns. --format json produces YAZA's versioned report; --format sarif produces SARIF 2.1.0 for code-scanning tools. Candidate secret values are omitted from both reports. The scan does not inspect dependency advisories, perform complete data-flow analysis, or prove an application safe. Use it as one review aid, not a security certification.
Part VI — Expert coding practice
The expert section focuses on concrete development work a reader can perform with current YAZA tools. It does not claim that YAZA is already memory-safe, asynchronous at the application level, production-ready on every target, or faster than competing runtimes.
Current development commands
| Command | What the learner can inspect | Important limit |
|---|---|---|
yaza check --format json |
Machine-readable compiler diagnostics for supported checks. | Diagnostic recovery and source-span precision remain partial. |
yaza test --format json |
Per-test compile/run/cleanup status and bounded test execution. | Test subprocess isolation is not an operating-system sandbox. |
yaza arch check |
Import edges against declared project layer rules. | It does not resolve external packages or every dynamic/generated dependency. |
yaza audit --security --format json / yaza audit --security --format sarif |
Selected risky source/configuration patterns as YAZA JSON or SARIF 2.1.0, with stable rule IDs and source locations. | It is heuristic and does not scan dependency advisories or prove runtime safety. |
yaza build --sanitize <file.yz> -o <binary> |
Native build with AddressSanitizer and UndefinedBehaviorSanitizer when supported by the host toolchain. | A clean run only covers the executed test paths and host. |
yaza bench <file.yz> <count> |
Repeated native runs of a single-file program. | It cannot benchmark API servers and is not a cross-runtime benchmark suite. |
Expert capstones
Advanced capstones will combine earlier lessons: a command-line program that parses bounded input and reports results, then an API project with validated requests, layered YAZA calls, PostgreSQL persistence, generated Postman/OpenAPI artifacts, and a documented test record. Every capstone will name which pieces are YAZA source and which still use native runtime services.
Closing note
Learning to program is a sequence of small experiments: read a line, predict what it does, run it, and compare the result. This book follows that approach from the first .yz file through command-line programs, APIs, and verified PostgreSQL examples. YAZA continues to evolve, so each chapter must earn its place with code a reader can run and evidence they can inspect. The goal is to help beginners become capable YAZA programmers while understanding the limits of the tools they use.
Glossary
| Term | Beginner definition |
|---|---|
| API | Rules that let one program request work or data from another. |
| Argument | A value supplied when a function or command is called. |
| Authentication | A check that identifies or verifies a caller. |
| Byte | A unit of binary data; a byte value can contain any of 256 possible bit patterns. |
| CLI | A command-line interface used by typing commands in a terminal. |
| Compiler | A program that checks source code and translates it into a form a computer can run. |
| Database | A system for storing and retrieving structured information. |
| Environment variable | A named value supplied to a process by its operating environment. |
| Expression | A piece of code that produces a value, such as 2 + 3. |
| Function | A named set of instructions that may take inputs and return a result. |
| HTTP | A standard request-and-response protocol used by web clients and servers. |
| Header | A named piece of information sent with an HTTP request or response. |
| JSON | A text format for structured data with objects, arrays, strings, numbers, Booleans, and null. |
| Migration | A versioned database change applied in a known order. |
| Module | A source file or group of files that provides reusable declarations. |
| Parameter | A named input declared by a function. |
| Query parameter | A value after ? in a URL, such as limit=10. |
| Request | A message a client sends to ask a server for data or an action. |
| Response | A server's reply to a request, including a status and optional body. |
| Route | A rule mapping an HTTP method and path to an API handler. |
| Shape | A YAZA declaration that groups named, typed fields. |
| Source code | Instructions written in a programming language for people to read and edit. |
| Status code | A number in an HTTP response that summarizes the outcome, such as 200 or 404. |
| Type | A rule describing what kind of value a variable or parameter may hold. |
| Validation | Checking that data follows the rules required by a program. |
| Value | A piece of data a program can store or work with. |
| Variable | A named place for a value while a program runs. |
Option |
A YAZA value that contains either a present value or none. |
Result |
A YAZA value that contains either a successful value or an error. |
Chapter publication standard
Do not teach a planned capability as working syntax. Before a hands-on chapter is accepted, require a complete runnable example, source implementation, focused test or integration evidence, documented limits, and a verified target. Run linked examples from a clean checkout and preserve their observed output. For API/database chapters, state service prerequisites and use the relevant integration check. Keep the language, CLI output, examples, and explanations in English; define unfamiliar terms in the glossary. When the manuscript stabilizes, render Word/PDF and review page breaks, links, code wrapping, headers, footers, and tables.
Manuscript changelog
- 2026-10-27: Added the verified
List[T]collection subset and safelist_try_getlesson to the pure-language section, with a runnable example, bounds behavior, and clear limits. Edition 0.6. - 2026-10-27: Made pure-language study explicit before the API sections, refreshed the verified target notes, and added the tested
Resultpropagation lesson for?, including its limits and runnable example. Edition 0.5. - 2026-10-26: Expanded the beginner opening with plain-language explanations of coding, files, folders, terminals, shells, paths, the CLI, and compiler feedback; renamed the author front matter to Preface and clarified that the manuscript's prose and exercises are original. Added worked explanations, output, and practice/check sections across Chapters 1–9. Edition 0.4.
- 2026-10-26: Turned the outline into a coding-only textbook front matter and course map; added an original YAZA cover, author, foreword, running header/footer specification, linked contents, current implementation scope, and expert-tool chapters; corrected the PostgreSQL CRUD path to match the verified YAZA adapter integration; added beginner lessons on text, lists, bytes, modules, command-line arguments, and files. Living manuscript advanced to edition 0.3.
- 2026-10-24: Expanded the curriculum through APIs, PostgreSQL, and advanced practice; added author and draft foreword, contents, and install-status notes; ran the API HTTP smoke script successfully with the repository CLI; recorded current layering and database boundaries; future topics remain curriculum only until implementation and verification.