Skip to content
FutureBlitz

    Install

    Guide

    Types and nullability

    The built-in types, conversions, aliases, Enums, and how T? keeps Null where it's written.

    FutureBlitz is statically typed: every value has a type the compiler knows. A variable’s type can be left out when it has a value to take it from; a parameter’s and a result’s are always written. The spec’s chapter on types has every rule.

    Numbers

    Int is 32 bits and Long 64, with Int8 to Int64 and UInt8 to UInt64 for the rest. Float is 32 bits and Double 64. Arithmetic between two types converts one to the other only when nothing’s lost, so an Int and a Long make a Long, and an Int and a Float need an As.

    Import std.core
    
    Local frames: Long = 1
    frames += 2_000_000_000          // fine: a Long holds it
    Local average = 15 As Float / 4
    Print(frames, average)
    
    Print(255 As UInt8, 256 As UInt8, -1 As UInt8)
    Print(0xFF, 0b1010, 0o17)
    Output
    2000000001 3.75
    255 0 255
    255 10 15

    In a debug build, overflow panics where it happens; in a release build it wraps. When wrapping is what’s meant, every integer has WrappingAdd and the rest, and CheckedAdd gives Null where it would overflow.

    Import std.core
    
    Local big: Int = 2_147_483_647
    Print(big.WrappingAdd(1))
    Print(big.CheckedAdd(1) ?? -1)
    Output
    -2147483648
    -1

    Aliases and Enums

    An Alias is another name for a type. An Enum is a set of named integers; @Flags makes them bits that combine.

    Import std.core
    
    Alias Scores = Int[]
    
    Enum State
        Idle, Walk, Attack, Dead
    End Enum
    
    @Flags Enum Layer
        World, Player, Pickup
    End Enum
    
    Local scores: Scores = [10, 20]
    Local state = State.Attack
    Local mask = Layer.World | Layer.Pickup
    
    Print(scores, state, state As Int)
    Print(mask, mask.Has(Layer.Player))
    Output
    [10, 20] Attack 2
    World | Pickup False

    Nullability

    No type holds Null unless it’s written with a ?. A String is always text; a String? is text or Null. (A String’s Methods, like Slice and Length, are std.text’s.) Code can’t use a T? as a T until it has checked, and the check narrows the variable’s type from there on.

    Import std.core
    Import std.text
    
    Function Initials(name: String?): String
        If name == Null Then Return "??"
        Return name.Slice(0, 1)              // name is a String here
    End Function
    
    Print(Initials("Grace"), Initials(Null))
    
    Local names: String?[] = ["Ada", Null, "Alan"]
    For n In names
        Print(n ?? "(nobody)", n?.Length ?? 0)
    End For
    Output
    G ??
    Ada 3
    (nobody) 0
    Alan 4

    Narrowing applies to Locals, Consts, and parameters. A Global or a Field can change during any call, so it isn’t narrowed: copy it into a Local and check that.

    • a?.b is Null when a is; a?.M() only calls M when a holds a value.
    • a ?? b gives a’s value, or b, which only runs when a is Null.
    • x.Expect("why") gives the value or panics with the message; x.OrElse(make) calls make for a value only when x is Null.
    • A T? can’t go in a string: write what Null shows as, "{name ?? "nobody"}".

    Report an issue with this page or with FutureBlitz on GitHub.