Docs contents

Syntax reference

Contents · First program: Language guide · Built-in functions

This reference describes High (.nagi). Some short examples are fragments for a function body. Use the complete introductory program to run several features together.

On this page

Files and indentation

  • Save as UTF-8 with a .nagi extension.
  • Put import/from statements, classes, functions, async functions, and external Rust declarations at the top level. Executable statements go inside functions.
  • Introduce a block with : and indent with spaces. Four spaces are recommended; tabs are forbidden.
  • Comments start with #. Identifiers use ASCII letters, digits, and _, and cannot begin with a digit. Unicode strings and comments are supported.
  • Expressions inside () and [] can span lines. Trailing commas are not supported.
def main():
    # A comment
    print("Hello, Nagi!")

Values and variables

Form Meaning
count = 10 Inferred type; integers default to i64
count: i32 = 10 Explicit type
rate = 1.5 Floats default to f64
enabled = True / False Boolean; lowercase true/false also accepted
name = "Nagi" / 'Nagi' UTF-8 string
values = [1, 2, 3] List of elements with one type
values: List[i64] = [] Type annotation for an empty list
missing: i64? = None Absent nullable value; null also accepted
present: i64? = some(42) Present nullable value
count = 11 Reassignment with the same type
count += 1 / -= 1 / *= 2 Compound assignment; /= and %= unsupported

String escapes are \n, \r, \t, \", \', and \\. There are no f-strings, interpolation, or triple-quoted strings. See types.

Functions and return

Calls resolve to a local function value, a user-defined function, then a builtin, in that order. Defining your own len makes len(...) call that function. Variables containing other values cannot be called.

Rust keywords such as type can be names wherever Nagi's grammar permits them. The compiler escapes names in generated Rust and preserves the original names in Low, JSON fields, and SQLite columns.

def add(a: i64, b: i64) -> i64:
    return a + b

def show(value: i64):
    print(value)
    return

Parameter types are required. An omitted return type means unit. Value-returning functions must return on every path. Calls use positional arguments, such as add(1, 2). Default/variadic arguments and user-defined generics are unavailable.

Branches and loops

This is a function-body fragment:

score = 80
if score >= 80:
    print("Passed")
else:
    print("Try again")

for index in range(3):
    print(index)

count = 0
while count < 3:
    count += 1

Conditions require bool. range(n) takes one argument and runs from zero up to but excluding n. List/view iteration supports primitives and Copy classes. elif, break, continue, and pass are unavailable.

Operators

The table runs from highest to lowest precedence. Binary operators on the same level parse left to right. Use parentheses where an expression is unclear.

Precedence Operator Example
Highest Call, field, index add(1, 2), point.x, values[0]
↓ Unary -, not, try, await -count, not enabled, try await db_open(...)
↓ *, /, % count * 2
↓ +, - count + 1
↓ <, >, <=, >= count < 10
↓ ==, != count == 10
↓ and count > 0 and count < 10
Lowest or enabled or count == 0

Negation, -value, accepts signed integers (i8/i16/i32/i64) and floating-point numbers (f32/f64). It does not accept functions or class values.

Values being compared == / != < / > / <= / >=
Numbers, bool, or str of the same type Supported Supported
UUID or timestamp Supported Unsupported
view[str] or view[bytes] Supported Supported
view[T] When T supports equality When T supports ordering
Classes or owned Lists Unsupported Unsupported

A borrowed list of classes, such as view[Point], cannot be compared directly either. Compare the fields you need. List views compare their elements.

Numeric types do not convert implicitly. Use i64(value) to widen i32. i32(an_i64) returns Result[i32, Error]; use forms such as try i32(value) inside a Result-returning function.

Use count > 0 and count < 10 rather than chained 0 < count < 10. Integer / is integer division. **, //, and bitwise operations are unsupported.

Classes, lists, and views

class Point:
    x: f64
    y: f64

def main():
    point = Point(x=1.0, y=2.0)
    print(point.x)
    values = [10, 20]
    append(values, 30)
    print(values[0])
    borrowed = view(values)
    duplicate = copy(borrowed)
    print(len(duplicate))

Construct classes with every field named. Assigning fields/indices, methods, and inheritance are unsupported. Indices start at zero; negative or out-of-range indices panic at runtime. Strings cannot be indexed. Borrow a string/list range with try slice(view(data), start, end).

Passing owned strings/lists to user-defined functions moves them. For read-only arguments, accept view[str] or view[i64] and pass view(value). See the guide and ownership.

Result, async, and scopes

Form Meaning
-> Result[i64, Error] Returns an integer or Error
return ok(42) Returns success
return error("reason") Returns failure
return not_found("reason") Missing target; HTTP 404
return fail(problem) Returns the original Error
value = try parse_i64("42") Extracts a value or returns failure to the caller
async def work(): Defines an async function
await sleep(10) Waits for 10 milliseconds
db = try await db_open(":memory:") Waits and propagates Result failure

Use try in Result-returning functions and await in async functions. Handle Result locally with both cases. This is a function-body fragment:

match parse_i64("42"):
    case Ok(number):
        print(number)
    case Err(problem):
        print(error_kind(problem))

Use _ for unused payloads. Matching consumes Result; names exist only in their case and cannot reuse outer variable names. See error handling for complete examples and limits.

Spawn child tasks inside a scope, as in this complete program:

async def main() -> Result[unit, Error]:
    async with scope:
        spawn sleep(10)
        spawn sleep(15)
    return ok(print("Done"))

Leaving a scope waits for its children. Returning inside it, passing views to another task, and spawning value-returning tasks are currently unsupported. See async.

Imports, HTTP, and Rust

Purpose Form Details
Load a file import "models.nagi" Imports; one shared namespace
Name a module import "orders.nagi" as orders Use that file's own definitions through orders.Order or orders.score(...)
Select a definition from "orders.nagi" import Order as SavedOrder One definition per statement; as SavedOrder is optional
Define a GET handler @get("/users/{id}") before a function HTTP; post/put/delete also available
Return HTML return ok(html("<h1>Hello</h1>")) Return type Result[Html, Error]
Embed text include_text("index.html") Relative to source; embedded at compile time
Declare a Rust function @rust("native::crc32"), then extern def crc32(text: view[str]) -> i64 Rust integration; no body or trailing colon

orders.Order and SavedOrder are the same type. Same-named classes from different files are different types. from and as are contextual import keywords and remain available as ordinary identifiers.

Differences from Python

Common Python form In Nagi
def add(a, b): Annotate parameters; add a return type when returning a value
print(a, b) print(a) and print(b) separately
items.append(x) append(items, x)
try: ... except: try expression propagates failure; match branches on Result
from models import User from "models.nagi" import User; quote the relative file path
Dictionaries, tuples, comprehensions, lambdas Unsupported; use classes, lists, ordinary functions, and loops
str(42) or arbitrary casts No general conversion; use forms such as print(42) directly

Nullable values support None/some(value), but not matching or a general unwrap API. Having type notation does not imply a complete operations API.

Release integer arithmetic follows the Rust backend's fixed-width behavior; overflow in operations such as addition wraps. Debug Rust builds may panic. A consistent language specification for checked/wrapping arithmetic is future work. See Low for its syntax/replacements and the roadmap for plans.

Nagi 0.1 documentationPage source