日本語

Values and Types

This section explains the values and types handled in LuneScript in detail.

Primitive Types

Type Name Description Example
int Integer 1, -10, 0x10
real Real number (floating point) 1.0, 3.14
bool Boolean true, false
str String (immutable) "hello", 'world'
stem All types except nil (Any non-nil value)

stem is the base type for all types, but it cannot hold nil. To allow nil, use stem!.

Collections

List<T> (List)

A dynamic array where elements can be added or removed.

let mut list: List<int> = [ 10, 20 ]; // Initialization
list.insert( 30 );  // Add: [ 10, 20, 30 ]
list.remove();      // Remove from end: [ 10, 20 ]
print( list[ 1 ] ); // Access (1-based index): 10
print( #list );     // Get size: 2
LuneScript (and Lua) list indices start from 1. Be careful, as out-of-bounds access results in undefined behavior.

Map<K,V> (Associative Array)

Holds key-value pairs. JSON-like literals can be used.

let mut map: Map<str,int> = {
    "a": 1,
    "b": 2
};
// Access
print( map[ "a" ] ); // 1
print( map.b );      // Can also access via dot notation if the key is a string

// Delete (by assigning nil)
map.a = nil;
The result of Map access is always Nilable (T!), because nil is returned if the value corresponding to the key does not exist.

Set<T> (Set)

Manages a collection of unique values. The literal is (@ ... ).

let mut set = (@ 1, 2, 3 );
set.add( 4 );       // Add
set.del( 1 );       // Delete
print( set.has( 2 ) );  // Check existence: true
print( set.len() );     // Number of elements: 3

Set operations or (union), and (intersection), and sub (difference) are also supported.

Nilable Types (Null Safety)

Appending ! to a type name makes it a type that can hold nil.

let val1: int = 1;   // Non-nilable
let val2: int! = nil; // Nilable (int or nil)

Nilable types cannot be used in calculations as is (for safety). They must be Unwrapped (Non-Nilated).

unwrap

Forcibly removes nil. If the value is nil, a runtime error occurs.

let a: int! = 10;
let b: int = unwrap a;

You can specify a default value for safe unwrapping.

let a: int! = nil;
let c: int = unwrap a default 0; // Becomes 0 if nil

if! let / when!

Syntax for executing a block only if a variable is not nil.

fn funcReturnsNilable(): int! { return 1; }
if! let x = funcReturnsNilable() {
    // x can be treated as non-nil (int) here
    print( x + 1 );
} else {
    print( "x was nil" );
}

when! checks if an existing variable is nil.

let val: int! = nil;
when! val {
    // Inside this block, val is of type int (immutable)
}

let! .. then ..

A style that describes the "processing when the variable is nil" first (similar to a guard clause).

fn funcReturnsNilable(): int! { return nil; }
let! x = funcReturnsNilable() {
    // Executed if x is nil
    return;
}
// x is non-nil here
print( x + 1 );

Nil Conditional Operator ($)

Performs member access or function calls on a nilable value only if it is not nil. If the value is nil, it returns nil. The evaluation result is always a Nilable type.

class Test {
    pub let val: List<int>;
    pub fn func(): int { return 100; }
}

fn sub( test: Test! ) {
    // If test is nil, everything becomes nil
    print( test$.val$[1], test$.func$() );
}

sub( new Test( [ 1, 2 ] ) ); // 1 100
sub( nil );                   // nil nil

Casting

There are two types of type conversion: @@ (forced) and @@@ (safe).

Forced Casting (@@)

let val: stem = 10;
let i = val@@int; // Conversion without checking. Behavior on failure is undefined or an error.

Safe Casting (@@@)

let val: stem = "test";
if! let i = val@@@int {
    print( "Integer!" );
} else {
    print( "Not an integer" );
}

Returns nil if conversion fails.

Algebraic Data Types

alge is a type of enumeration, but each value (constructor) can have parameters. It is a tagged union.

alge Shape {
    Rect( real, real ),
    Circle( real ),
}

let s = Shape.Rect( 10.0, 20.0 );

match s {
    case .Rect( w, h ) { print( "Rect:", w * h ); }
    case .Circle( r ) { print( "Circle:", r * r * 3.14 ); }
}

Enum

A simple enumeration of values. Integers, real numbers, and strings can be assigned.

enum Color {
    Red = 0,
    Green, // 1 (Auto-incrementing)
    Blue,  // 2
}
print( Color.Red ); // 0
print( Color.Red.$_txt ); // "Color.Red" (String representation)

Use _from to convert a raw value to an Enum.

enum Color { Green=1 }
let c = unwrap Color._from( 1 ); // Color.Green (Needs unwrap because it is nilable)

Tuple

Handles multiple values together.

let t: (int, str) = (= 1, "ok" );

Use ... to expand it, for example, when passing arguments to a function.

fn func( i: int, s: str ) { print( i, s ); }
let t: (int, str) = (= 1, "ok" );
func( t... );