Documentation / Language basics

Language basics

Moth keeps a small semantic surface. Each bit of punctuation or keyword represents a single concept.

This page covers the syntax you will see across Moth codebases.

Choose the explanation level: Blocks and statements

Blocks and statements

Most Moth blocks have a colon at the start and a semicolon at the end.


    greet |name String|:
        io.line([: Hello, [name]])
    ;
    

The colon opens the function body. The semicolon closes it.

Semicolons do not end every line. Add one only when the surrounding block is finished.

Use indentation to make nesting easy to see:


    if ready:
        io.line("Ready!")
    ;
    

Moth uses explicit block delimiters.

A colon opens a block. A semicolon closes that block.


    clamp_to_zero |value Int| -> Int:
        if value < 0:
            return 0
        ;

        return value
    ;
    

Contract

  • : opens a function body, branch, loop or another block-form construct.
  • ; closes the active block.
  • A semicolon does not terminate an ordinary statement.
  • Statements inside a block are evaluated in source order unless control flow exits or redirects the block.
  • Child blocks create child scopes.
  • Indentation is for readability. Explicit delimiters define block boundaries.

Reading the shape

The final semicolon in the example closes the function. The earlier semicolon closes the if block.

Nested language features may add their own structural rules. Branching, loops, functions and template control flow document those forms in detail.

Choose the explanation level: Comments and naming

Comments and naming

In ordinary Moth code, comments begin with two minus signs:


    -- This source-code line is ignored by the compiler.
    score = 10
    

Template and moth template bodies are content. Two minus signs inside those bodies are text rather than source comments.

Use regular_snake_case for values and functions:


    player_score = 10

    double_score |score Int| -> Int:
        return score * 2
    ;
    

Use PascalCase for named types:


    PlayerScore = |
        value Int,
    |
    

Clear names beat short names. player_score is easier to understand than ps, especially when the code grows.

In ordinary Moth code, -- starts a single-line comment.


    -- Explain why this value exists.
    retry_count = 3
    

Everything after -- on that source-code line is comment text.

Template and moth template text

Template bodies are content, not ordinary code-token context.

Inside a template body, -- is ordinary body text. A moth template .mtf file is the body of an implicit compile-time Markdown template, so -- is text there too.

Use template directives such as $note or $todo when template-authored content should be ignored.

Naming conventions

  • Variables and functions use regular_snake_case.
  • Structs, choices, type aliases and generic parameters use PascalCase.
  • Trait names use all-caps identifiers such as DISPLAY_TEXT.
  • Names should describe their role rather than their storage or implementation detail.

    retry_count = 3

    load_profile |user_id String| -> String:
        return "profile"
    ;

    UserProfile = |
        display_name String,
    |
    

Name collisions

A visible name cannot be redeclared while it remains in scope.

The complete no-shadowing contract is documented by Values and bindings. Import aliases and module API names have additional collision rules in the Packages documentation.

Choose the explanation level: Core values

Core values

Moth can usually work out a value's type from the value itself.


    enabled = true
    count = 42
    ratio = 0.5
    letter = '🦋'
    name = "Moth"
    

You can write the type when it helps the reader or gives the compiler needed context:


    count Int = 42
    ratio Float = 0.5
    name String = "Moth"
    

The most common core types are:

  • Bool for true and false
  • Int for whole numbers
  • Float for decimal numbers
  • Char for one character
  • String for text

Collections, structs, choices and options build larger values from these ideas.

Values have a semantic type. A binding may infer that type from its initializer or state it explicitly.


    enabled = true
    count Int = 42
    ratio Float = 0.5
    letter Char = '🦋'
    label String = "Moth"
    

Core forms

  • true and false are Bool values.
  • Whole-number literals are naturally Int.
  • Decimal-point literals are naturally Float.
  • Single-quoted literals create Char values.
  • Quoted slices and string templates use the String type.
  • none requires an optional T? receiving context.

The Numbers documentation owns exact numeric grammar and arithmetic behaviour. The Strings and characters section owns the implemented string source forms. Options are documented with Errors and options.

Composite values

Collections use braces. String templates use square brackets.


    names = {"Priya", "Grace"}

    message = [:
        Hello from a template.
    ]
    

Structs, choices, collections, maps and generic instances add richer value shapes without changing the basic annotation form.

Binding mutability and access mode are separate from semantic type identity. See Values and bindings for that contract.

Choose the explanation level: Strings and characters

Strings and characters

Use double quotes for normal text:


    greeting = "Hello"
    

Expression-position backtick strings aren't implemented. Use an ordinary string with escapes when the text contains backslashes:


    windows_path = "C:\\docs\\site"
    

Use single quotes for one character:


    seedling = '🦋'
    

Use a template when text needs values or a larger structure:


    name = "Priya"

    greeting = [:
        Hello, [name]
    ]
    

Joining strings

Use templates to join strings together. Put the parts inside square brackets:


    first = "Hello, "
    second = "world"
    message = [first, second]
    

Don't use + to join strings. + is for numbers only. Use templates to concatenate strings.

Read-only strings

A quoted string like "Hello" is read-only content. You can reassign a mutable binding to point at a different string, but you can't change the string itself.

Square brackets belong to templates. Collections use curly braces instead.

Moth has two string source forms and one character form. Both string forms share one semantic String type at ordinary typed boundaries.


    escaped_slice = "Hello\nworld"
    letter = '🦋'

    name = "Priya"
    message = [:
        Hello, [name]
    ]
    

Quoted string slices

Double quotes create escaped string slices. They support exactly these escapes:

  • \\ for a backslash
  • \" for a double quote
  • \n for a newline
  • \r for a carriage return
  • \t for a tab

Every other escape is invalid. A backslash can't continue a quoted string across a physical newline.

A quoted string slice is read-only content. It has no character or substring mutation surface and isn't concatenated with +. A slice may be read, compared, passed, returned, stored and inserted into templates. A mutable binding that currently holds a slice may be reassigned. That doesn't make the slice contents mutable.


    name ~= "Priya"
    name = "Aisha"  -- reassigns the mutable binding, doesn't mutate the slice
    

Characters

Single quotes create Char values.

Template strings

Square brackets create string templates. Templates produce owned string values and may fold at compile time or lower to runtime string construction.

Templates are the idiomatic and canonical way to:

  • concatenate strings
  • interpolate values
  • build runtime or compile-time text
  • produce strings that need full template composition

    first = "Hello, "
    second = "world"
    message = [first, second]

    name = "Priya"
    greeting = [: Hello, [name]]
    

Use [left, right] for concatenation. Don't use +:


    -- VALID: template concatenation
    joined = [left, right]

    -- INVALID: + is a numeric operator only
    joined = left + right
    

Source String + String is invalid. The accepted rule is that + is a numeric operator only and the compiler should reject all string + forms. The current compiler still accepts source string + and construction-origin-dependent equality checks. Stage B removes both. See progress matrix for the current implementation gap.

One semantic String type

Quoted slices and template-produced strings share one String type at ordinary typed boundaries. They share:

  • parameter and return compatibility
  • equality
  • choice payload equality
  • collection and map value use
  • String map-key use
  • casts
  • IO and external StringContent boundaries
  • template insertion
  • aliases and generic use where String is concrete

Construction origin doesn't create hidden equality, hashing, map-key or call-compatibility rules.

Equality

String supports is and is not. String doesn't support ordering operators (<, <=, >, >=).


    slice = "hello"
    template = [: hello]

    -- VALID: equality across both forms
    same = slice is template

    -- INVALID: ordering is not supported
    -- ordered = slice < template
    

Binding reassignment versus content mutation

A mutable binding that holds a string can be reassigned. Quoted slices are immutable. Templates produce owned String values. The current source surface does not yet expose in-place string mutation operations.


    text ~= "initial"
    text = "replacement"  -- valid: reassigns the binding

    -- No mutation API exists:
    -- text.append("more")  -- invalid
    -- text[0] = 'X'        -- invalid
    

Templates

Templates own interpolation, formatting, slots, wrappers and structured content. See Templates for the complete system.

Quoted slices and templates both participate in the language's String surface. Expression-position raw backtick slices aren't implemented. Inside a template body, backticks, backslashes and physical newlines are ordinary body text available to the active formatter.