Questionable
Questionable Types (?)
Questionable Types (?)In Helix, questionable types are used to represent values that might hold one of three states:
- A valid value.
null
(indicating the absence of a value).- 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 TypesA 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 BehaviorsUsing Questionable Types
Using Questionable TypesYou 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 Checks1. 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 inTo 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 ChecksUse == 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 TypesA questionable type can hold one of three states:
let x: int? = 42;
let y: int? = null;
- errors can not be assigned directly. Since they are the panicked state of a function.
- They can be assigned as a result of a function call. but not directly.
- refer to Panic for more information.
let x: int? = divide(10, 0); // Holds an error
Using Questionable Types in Functions
Using Questionable Types in FunctionsReturning Questionable Types
Returning Questionable TypesFunctions 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 ArgumentsFunctions 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 Examplesfn 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.");}
fn calculate_area(length: int, width: int) -> int?: if length <= 0 || width <= 0: panic error::InvalidArgumentError("Dimensions must be positive."); return length * width;
fn main(): let area: int? = calculate_area(5, -3);
if error::InvalidArgumentError in area: print("Invalid dimensions provided."); elif area?: print(f"Area: {area}"); else: print("Unexpected issue.");}
import math;
fn sum_valid_numbers(numbers: list::<int?>) -> int { let total: int = 0;
for num in numbers { if num?: total += num; }
return total;}
fn get_random_number() -> int? { if math::random::<int>(0, 100) % 2 == 0: return math::random::<int>(0, 100); else if math::random::<int>(0, 100) % 3 == 0: return null; else: panic error::ParseError("Random error");}
fn main() { let values: list::<int?>;
// Generate 10 random numbers for _ in 0..10 { values.push(get_random_number()); }
let sum = sum_valid_numbers(values); print(f"Sum of valid numbers: {sum}");}
Advantages of Questionable Types
Advantages of Questionable TypesCommon Mistakes
Common MistakesSkipping Validity Checks
Skipping Validity ChecksProblem: 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
ConclusionQuestionable 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.