Foundation Source format

Lexical Structure

Klyn source files are intentionally simple to read: indentation defines blocks, comments use #, and most punctuation exists only where it improves clarity. This page covers the lexical rules you need before writing larger programs.

A Klyn Source File

#!/usr/bin/env klyn

package my.app

import klyn.math

const LIMIT = 10

def compute() as Int:
    return Int(Random.random() * LIMIT)

Not every file needs every part. The shebang is optional. Omitting the package declaration places the source in the default package; its directory never creates an implicit package. A declared package must match the source directory. Imports appear only when you need packages outside the automatically imported base package klyn.

Comments

Line comments start with #. KlynDoc-style documentation comments use the familiar /** ... */ form.

# Single-line comment
value = 10

/**
 * Documentation comment for a public API.
 */
public class Counter:
    pass

Indentation and Blocks

A block starts after a trailing :. The following lines must be indented with spaces, with four spaces per nesting level. Misaligned indentation, forbidden tabs and missing indented blocks raise IndentationError, a subclass of SyntaxError. A handler for SyntaxError also catches these errors. A missing colon or a body written on the same line after the colon remains a SyntaxError.

if total > 0:
    print("positive")
else:
    print("zero or negative")
Important

Keep top-level code flush-left. Leading indentation before a top-level statement is rejected, and tabs should not be used as indentation.

Block Scope

A variable belongs to the block in which it is first declared. It is visible from nested blocks, but never from a parent or sibling block. This rule applies consistently to if, for, while, try, catch, finally, and every branch of a match. A for binding is local to its loop.

total = 0

for value in [10, 20, 30]:
    doubled = value * 2
    total += doubled

print(total)    # 120
# print(value)  # SyntaxError: value is outside its scope

An assignment targets the nearest visible variable. Declare shared state before entering a control-flow block when that block must update it. A name first assigned inside the block instead belongs to that block and disappears when execution leaves it.

calculationRequired = true
result = 0

if calculationRequired:
    result = 42                 # Updates the enclosing variable.
    elapsed = 5                 # Declares a variable local to this branch.

print(result)
# print(elapsed)               # SyntaxError: elapsed is outside its scope.

Sibling branches never share declarations made inside one another. For a computed selection, prefer a match expression so the compiler can infer one compatible result type without leaking branch-local names.

status = 200

label = match status:
    case 200:
        "success"
    case 404:
        "not found"
    default:
        "error"

Identifiers and Naming

Kind Typical form Examples
Types UpperCamelCase Rational, TimeZone, ArrayList
Functions and methods lowerCamelCase timeMs, toString
Variables and properties lowerCamelCase count, userName
Private backing fields leading underscore _value, _items

Basic Literals

count = 10
unsignedCount = 10u
ratio = 0.5
floatRatio = 0.5f
halfRatio = 0.5h
notANumber = nan
singleInfinity = inff
utf16Unit = `A`
initial = 'A'
smile = '😀'
name = "Klyn"                  # String: immutable text.
editableName = m"Klyn"         # MutableString: editable text.
message = """First line
Second line
Third line"""

Integer literals default to Int. Floating-point literals default to Double. Explicit suffixes such as u, f (Float), and h (Float16) are useful when you want a more precise literal type at the source level. Klyn also reserves nan, nanf, nanh, inf, inff, and infh for special floating-point values; their exact types and comparison rules are detailed in Typing and Constants.

Backticks delimit one 16-bit CodeUnit, while apostrophes delimit one Unicode scalar Char. A Char can therefore hold a supplementary character such as 😀 without splitting it into UTF-16 surrogates. See CodeUnit, Char, and Unicode for conversions and the string indexing model.

Double quotes create an immutable String. Add m immediately before the opening quote to create a MutableString whose characters can be edited, for example editableName[0] = 'k'. Triple-double-quoted strings support the same prefix. Their common leading margin is removed when the literal is compiled, while relative indentation is preserved. The first line after the opening delimiter is left unchanged and is excluded from the margin calculation. Subsequent nonempty lines determine that margin; when the closing quotes are on their own line, their indentation also bounds the removable margin. Closing at column one preserves all leading indentation. The column of the opening quotes does not affect the result. This rule applies to plain, raw, mutable and formatted text blocks alike, regardless of the words they contain. Escapes are interpreted after this margin removal. This choice is independent of const, which fixes a reference rather than making its contents immutable. See Immutable and Mutable Text for conversions, aliases, and snapshots, or Strings and Interpolation to combine m with other string prefixes.

text = """Title
             First item
                 Nested item
             Last item"""
assert text == "Title\nFirst item\n    Nested item\nLast item"

# Anchor an isolated block with the indentation of the closing delimiter.
code = """def answer() as Int:
              return 42
          """
assert (Application.eval(code + "\nanswer()") as Int) == 42

Keywords and Contextual Words

The following case-sensitive words have a grammatical meaning in Klyn. Reserved words cannot be used as identifiers. Contextual words are interpreted as keywords only in the corresponding grammatical position; for example, get, set, and property can also name ordinary variables.

abstract and annotation as assert break case catch class const continue def default elif else emit enum event extends false final finally for get if implements import in inf inff infh interface is lambda match nan nanf nanh native not notnull null operator or out override package pass private property protected public query readonly ref return set signal static step super this throw throws to true try virtual while writeonly yield

Next Step

Continue with Packages and Imports before splitting code across multiple files or packages.