Skip to content

Questionable

Questionable Types (?)

Questionable Types (?)

In Helix, questionable types are used to represent values that might hold one of three states:

  1. A valid value.
  2. null (indicating the absence of a value).
  3. An error (indicating an exceptional state).

This feature integrates error handling and null checks directly into the type system, making Helix code concise, robust, and expressive.


Declaring Questionable Types

Declaring Questionable Types

A questionable type is created by appending ? to a base type. For example:

  • int?: A questionable integer.
  • string?: A questionable string.
let a: int?; // `a` can hold an `int`, `null`, or an error.

Key Behaviors

Key Behaviors

Using Questionable Types

Using Questionable Types

You can use a questionable type like a regular variable. If you use it in a context where a non-questionable type is required, Helix implicitly checks its validity:

  • If the value is valid, it proceeds.
  • If the value is null or an error, the program stops with a crash.
fn print_number(n: int):
print(n);
let x: int? = 42;
print_number(x); // Works because `x` has a valid value.
let y: int? = null;
print_number(y); // crash: `NullError`.

Validity Checks

Validity Checks

1. Broad Check with ...?

1. Broad Check with ...?

Use ...? to check if a questionable type holds a valid value:

let x /* inferred: int? */ = input("Enter a number: ") as int?;
if x?:
print(f"Valid number: {x}");
else:
print("Invalid input.");

2. Specific Error Detection with in

2. Specific Error Detection with in

To detect specific errors, use the in keyword:

let result: int? = divide(10, 0);
if error::ParseError in result:
print("Division by zero!");
else if result?:
print(f"Result: {result}");
else:
print("Unknown error occurred.");

3. Explicit null Checks

3. Explicit null Checks

Use == null or compare directly to null to detect null states:

let value: int? = null;
if value == null:
print("Value is null.");
else if value?:
print(f"Value is {value}");
else:
print("An error occurred.");

Assigning Questionable Types

Assigning Questionable Types

A questionable type can hold one of three states:

let x: int? = 42;

Using Questionable Types in Functions

Using Questionable Types in Functions

Returning Questionable Types

Returning Questionable Types

Functions that may fail or return no value should use questionable types:

fn divide(a: int, b: int) -> int? {
if b == 0:
panic error::ParseError("Division by zero");
return a / b;
}

Accepting Questionable Arguments

Accepting Questionable Arguments

Functions can accept questionable types as arguments and validate them inside:

fn process_number(n: int?) {
if n?:
print(f"Processing number: {n}");
else:
print("Invalid number or error.");
}

Practical Examples

Practical Examples
fn main() {
let input_number: int? = input("Enter a number: ") as int?;
if input_number?:
print(f"You entered: {input_number}");
else:
print("Invalid input. Please enter a valid number.");
}

Advantages of Questionable Types

Advantages of Questionable Types

Common Mistakes

Common Mistakes

Skipping Validity Checks

Skipping Validity Checks

Problem: Using questionable types without validation.

let x: int? = null;
print(x); // crash: `NullError`.

Solution: Always check validity with ...? before using:

if x?:
print(x);

Conclusion

Conclusion

Questionable types (?) in Helix are a powerful tool for managing nullable and error-prone data. By embedding error and null handling into the type system, Helix enables clean, concise, and safe code. Use ...?, in, and error in ... to validate questionable types and write robust programs.

References

References