Core syntax Static typing

# Typing and Constants

Klyn is statically typed from top to bottom. Type inference keeps declarations compact, but the inferred type is still a real compile-time type. This page explains the practical rules that matter when you declare variables, annotate APIs, and rely on constants.

Klyn Is Always Typed

    count = 10          # inferred as Int
    ratio = 0.5         # inferred as Double
    name = "Klyn"       # inferred as String

    count = 12          # valid
    count = "twelve"    # invalid

Inference is convenient syntax, not dynamic typing. The compiler remembers the type and checks every later assignment against it.

Explicit Type Annotations

    import klyn.collections

    count as Int = 10
    price as Double = 19.99
    names as ArrayList<String> = ["Ada", "Grace"]

Use explicit annotations when the type is part of the design, when a public API must stay self-documenting, or when you want a specific numeric type instead of the default literal type.

Numeric Types

Family | Types | Notes
--- | --- | ---
Signed integers | `SByte`, `Short`, `Int`, `Long` | Use `Int` by default unless you have a clear range or ABI reason not to.
Unsigned integers | `Byte`, `UShort`, `UInt`, `ULong` | Literal suffixes such as `10u` make unsigned intent explicit.
Floating-point | `Float`, `Double` | Floating literals default to `Double`; use `0.5f` for `Float`.

No silent narrowing

Klyn rejects assignments that would lose precision or violate the target type range.

Constants with `const`

    const PI = 3.141592653589793
    const APP_NAME as String = "Klyn"

    def demo():
        const LIMIT as Int = 3
        print(LIMIT)

Constants may be declared at module level or locally. They cannot be reassigned, incremented, decremented, or modified through compound assignment.

Null References

    message as String = null

    if message is null:
        print("no message")
    else:
        print(message)

Reference values may be `null`. Use `is null` and `is not null` for clear null checks.

Generic Types in Declarations

    import klyn.collections

    names as ArrayList<String> = ["Ada", "Grace"]
    config as Map<String, Int> = {"port": 8080}
    values as IList<Int> = i:[1, 2, 3]
    pair as Tuple<String, Int> = ("age", 42)

Generic arguments are part of the declared type. Literals often infer them automatically, but explicit generic annotations are usually clearer in public APIs. In Klyn, `IList<T>` is the immutable homogeneous collection contract, while `Tuple<...Ts>` denotes a fixed positional tuple.

Inspecting a Type Quickly

    value = [10, 20, 30]
    metadata = type(value)
    print(metadata.fullName)

The full reflection API is documented later, but `type(...)` is already useful when you want to verify what the compiler or runtime considers the real type of a value.

Common Mistakes

- Expecting inference to allow later assignments of unrelated types.
- Forgetting that `const` also forbids `+=`, `++`, and similar updates.
- Relying on implicit numeric narrowing instead of converting explicitly.
- Leaving externally visible properties untyped because “the compiler can infer it anyway”.