exprv4:reference:type_system



Type System

Expr is dynamically typed. A variable does not have a fixed declared type; the value stored in it carries its type at runtime.

a = 10;        // number
b = 'text';    // string
c = none();    // none

The same variable can later hold a different kind of value:

value = 123;
value = 'ready';

This page defines the core value types, truth rules, conversion behavior, and invalid-number behavior used by Expr.


Core Types

Expr has these core runtime value categories:

  • `None`
  • `Number`
  • `String`
  • `Object`

There is no separate stored boolean type. The literals `true` and `false` behave as numeric values `1` and `0`.

a = true;      // numeric true value
b = false;     // numeric false value

Objects include user-defined class instances and built-in object types such as arrays and maps.


None

`none()` represents no value.

value = none();
value.is_none();

`none()` is used when a value is missing, unavailable, or intentionally cleared.

Reading an unknown variable also produces `none()`:

missingValue;

Assigning `none()` to a variable clears or unsets that variable:

a = 10;
a = none();
a;              // none()

`none()` is not the same as `0`, an empty string, or `false`.

none() == 0;       // false
none() == '';      // false
none() == none();  // true

Do not use `none()` as a numeric value. Arithmetic with `none()` is an error.

5 + none();        // error

Number

Numbers are 64-bit floating-point values.

a = 10;
b = 3.14;
c = -0.5;
d = 1e3;

Numbers are used for arithmetic, comparisons, loop counters, coordinates, feed rates, and most numeric calculations.

radius = 5;
area = pi() * radius ** 2;

The literals `true` and `false` are represented as numeric true and false values:

true == 1;     // true
false == 0;    // true

Use value methods to check number state:

value.is_num();
value.is_num_notnan();
value.is_num_int();

NaN

`nan()` is a numeric value meaning not a valid number.

value = nan();
value.is_nan();

NaN is still a number for type-check purposes:

value = nan();
value.is_num();          // true
value.is_num_notnan();   // false

Typical sources of NaN include:

  • explicit `nan()`
  • failed numeric parsing, such as `'abc'.parse_num()`
  • invalid numeric results

NaN often propagates through arithmetic:

value = nan() + 1;
value.is_nan();          // true

Use `.is_num_notnan()` before calculations that require a valid finite number.

if(value.is_num_notnan())
{
    result = value * 2;
};

Bitwise and shift operations require finite numeric values. NaN is rejected by those operations.


String

Strings are text values.

name = 'Tool 1';
message = "Ready";
template = `Value`;

Strings support methods for text operations and parsing.

'  text  '.trim();
'abc'.upper();
'255'.parse_num();
'true'.parse_bool();

The `+` operator concatenates when a string is involved:

'Tool ' + 3;      // 'Tool 3'
'A' + 'B';        // 'AB'

Other arithmetic operators require numeric operands.

'10' * 2;         // error
'10'.parse_num() * 2;   // 20

Object

Objects group behavior and, depending on object type, stored data.

Objects include:

  • user-defined class instances
  • arrays
  • maps
  • host-provided objects

Example:

items = array();
items.add('A');
items.add('B');
items.to_string();

Objects are usually used through methods:

object.Method(arguments);

Some objects expose readable properties:

tool.Number;

Exact object behavior is documented in object-related reference chapters.

Callable References

Callable references are objects that refer to callable Expr behavior.

Expr currently uses callable reference objects for:

  • `function_ref` - reference to a user-defined function.
  • `builtin_ref` - reference to a built-in function.

A callable reference can be stored in a variable and called with its `call(…)` method:

function Add(a, b)
{
    return a + b;
}
 
f = Add;
f.call(2, 3);      // 5

Built-in functions can also be referenced:

f = str_spaces;
f.call(3);         // '   '

Callable references are still Object values in the type system. They are not a separate core type like Number or String.

Host objects can use callable references as callbacks:

function OnClick()
{
    print('clicked');
}
 
button.on_click(OnClick);

The type system defines that callable references are objects. The exact callback methods, event arguments, and lifetime rules belong to the relevant object or host-integration reference pages.


Truthiness Rules

Boolean contexts convert values to true or false.

Boolean contexts include:

  • `if`
  • `while`
  • `for` conditions
  • conditional operator `?:`
  • logical operators &&, ||, ^^, and !

Truthiness rules:

Value Truth behavior
finite non-zero number true
zero false
NaN or infinite number false
string `'true'` true
string `'TRUE'` or other case variants of `'true'` true
any other string false
`none()` false
object false by default

Examples:

if(1)       { result = 'true'; };     // runs
if(0)       { result = 'false'; };    // does not run
if('true')  { result = 'true'; };     // runs
if('hello') { result = 'false'; };    // does not run
if(none())  { result = 'false'; };    // does not run
if(nan())   { result = 'false'; };    // does not run

Use explicit checks when possible. They make scripts easier to read and safer to change.

if(speed.is_num_notnan() && speed > 0)
{
    print('valid speed');
};

Type Check Methods

Type checks are value methods. They are called on the value being checked.

Common type-check methods:

Method Meaning
`x.is_none()` true when `x` is `none()`
`x.is_nan()` true when `x` is numeric NaN
`x.is_num()` true when `x` is a number, including NaN
`x.is_num_notnan()` true when `x` is a finite usable number
`x.is_num_int()` true when `x` is an integer numeric value
`x.is_string()` true when `x` is a string
`x.is_bool()` true for finite numbers and for strings `true` or `false`, case-insensitive

Examples:

none().is_none();
nan().is_nan();
(42).is_num_notnan();
'abc'.is_string();

Parentheses are useful when calling methods on numeric literals:

(42).is_num();

Conversion and Parsing Methods

Conversions are exposed as methods on values. They are not global conversion functions.

Use this style:

value.to_string();
text.parse_num();

Do not write old-style conversion functions such as:

to_str(value);     // not current style
to_num(text);      // not current style

Number Methods

Common number conversion methods:

Method Result
`x.to_string()` decimal string representation
`x.to_bool()` boolean-style numeric result
`x.to_int()` signed integer conversion
`x.to_uint()` unsigned integer conversion
`x.to_s8()` signed 8-bit integer conversion
`x.to_u8()` unsigned 8-bit integer conversion
`x.to_s16()` signed 16-bit integer conversion
`x.to_u16()` unsigned 16-bit integer conversion
`x.to_s32()` signed 32-bit integer conversion
`x.to_u32()` unsigned 32-bit integer conversion
`x.to_s64()` signed 64-bit integer conversion
`x.to_u64()` unsigned 64-bit integer conversion
`x.to_hex()` hexadecimal string
`x.to_bin()` binary string
`x.chr()` character string for the numeric code
`x.bit(index)` selected bit value

Examples:

(255).to_string();
(255).to_hex();
(5).bit(0);

String Parsing Methods

Common string parsing methods:

Method Result
`s.parse_num()` parses numeric text
`s.parse_hex()` parses hexadecimal text
`s.parse_bin()` parses binary text
`s.parse_bool()` parses boolean-style text
`s.to_string()` returns the string value

Examples:

'255'.parse_num();
'FF'.parse_hex();
'1010'.parse_bin();
'true'.parse_bool();

Parsing can be chained with numeric conversion methods:

'255'.parse_num().to_u8();

Operator Coercion Summary

Operators do not all convert values the same way.

Important rules:

  • `+` performs numeric addition for two numbers.
  • `+` performs string concatenation if a string is involved.
  • -, *, /, %, and ** require numeric operands.
  • `==` and `!=` do not perform cross-type equality coercion.
  • `>`, `>=`, `<`, and `⇐` require numeric operands.
  • `!` uses truthiness.
  • && and || short-circuit and use truthiness.
  • ^^ is logical XOR and uses truthiness-compatible operands.

Examples:

1 + 2;              // 3
'A' + 10;           // 'A10'
1 == '1';           // false
'10'.parse_num() == 10;   // true

See Operators and Expression rules for exact operator behavior.


Practical Guidance

For reliable scripts:

  • Use `none()` for missing optional values.
  • Use `nan()` for invalid numeric results.
  • Use `.is_num_notnan()` before important numeric calculations.
  • Parse strings explicitly before numeric use.
  • Do not rely on cross-type equality conversion.
  • Prefer explicit checks over clever truthiness in machine workflow scripts.

Example:

function RequireFeedRate(value)
{
    if(!value.is_num_notnan())
    {
        error('Feed rate must be a valid number');
    };
 
    if(value <= 0)
    {
        error('Feed rate must be positive');
    };
 
    return value;
}

Previous: Lexical basics

Next: Variables and assignment

exprv4/reference/type_system.txt · Last modified: by 127.0.0.1

Page Tools