Type System
GLaDOS features a static, strong type system that ensures type safety at compile time. This document provides a comprehensive reference for the type system.
Primitive Types
GLaDOS supports four primitive types:
Integer Type (int)
- Size: 64-bit signed integer
- Range: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
- Haskell Representation:
Int64 - Literals: Decimal integer literals (e.g.,
0,42,-100,999999)
Examples:
int x = 42;
int negative = -10;
int zero = 0;
int large = 1000000;
Operations:
- Arithmetic:
+,-,*,/ - Comparison:
==,!=,<,>,<=,>= - No implicit conversion to/from bool or float
Float Type (float)
- Size: 64-bit double-precision floating-point
- Range: Approximately ±1.7 × 10^308 (IEEE 754 double precision)
- Precision: ~15-17 significant decimal digits
- Haskell Representation:
Double - Literals: Decimal floating-point literals (e.g.,
3.14,-0.5,1.0,2.5e10)
Examples:
float pi = 3.14159;
float negative = -2.5;
float small = 0.001;
float scientific = 1.5e-10;
Operations:
- Arithmetic:
+,-,*,/ - Comparison:
==,!=,<,>,<=,>= - No implicit conversion to/from int or bool
Important Notes:
- Float literals must include a decimal point (e.g.,
1.0, not1) - Division by zero results in runtime error (like integers)
- Floating-point arithmetic follows IEEE 754 standard
- No automatic conversion between
intandfloat
Type Safety with Floats:
float x = 3.14;
float y = 2.0;
float z = x + y;
int a = 5;
float b = a;
float c = 3.14 + 5;
Boolean Type (bool)
- Values:
trueorfalse - Haskell Representation:
Bool - Usage: Conditions, logical operations, comparison results
Examples:
bool flag = true;
bool isReady = false;
bool result = 5 > 3;
Operations:
- Logical:
&&(AND),||(OR) - Comparison:
==,!= - No implicit conversion to/from int or float
Void Type (void)
- Purpose: Indicates functions that do not return a value
- Usage: Only valid as a function return type
- Haskell Representation:
TypeVoid
Examples:
void printMessage() {
}
void doSomething(int x) {
if (x > 0) {
return;
}
}
Restrictions:
void x;
void result = someFunction();
void helper(int a, int b) {
}
Important Notes:
- Void functions do not need explicit return statements
- Can use
return;(without value) for early exit - Cannot be used as variable type
- Cannot be used as parameter type
- Type checker does not require return path analysis for void functions
Type Safety
Static Type Checking
All types are checked at compile time before the program runs. This catches type errors early:
int x = true;
float y = 5;
int x = 42;
float y = 3.14;
bool flag = true;
Strong Typing
GLaDOS does not allow implicit type conversions:
int x = 5 + true;
float y = 3.14 + 5;
if (5) {
return 1;
}
int z = 3.14;
if (5 > 0) {
return 1;
}
float a = 3.14;
float b = a + 2.0;
Type Inference
GLaDOS supports type inference using the var keyword. Variables declared with var must be initialized, and their type is inferred from the initialization expression:
var x = 42; // Inferred as int
var y = 3.14; // Inferred as float
var z = true; // Inferred as bool
int x = 42; // Explicit type declaration (also valid)
Requirements for var:
- Must be initialized at declaration (required)
- Type is inferred from the initialization expression
- Provides compile-time type safety
Note: The compiler uses an internal TypeInfer type during type checking for intermediate expressions to enable the var keyword functionality.
Function Types
Functions have a type signature consisting of:
- Return type: Type of value returned
- Parameter types: Types of all parameters (in order)
Function Signature Syntax
<return_type> <function_name>(<param_type> <param_name>, ...)
Examples:
int square(int x) {
return x * x;
}
int add(int a, int b) {
return a + b;
}
float multiply(float x, float y) {
return x * y;
}
bool isGreater(int a, int b) {
return a > b;
}
int getFortyTwo() {
return 42;
}
void printNumber(int x) {
}
Type Checking Rules
- Argument Count: Must match parameter count exactly
- Argument Types: Must match parameter types exactly (in order)
- Return Type: All return statements must match declared return type
int add(int a, int b) {
return a + b;
}
int main() {
int x = add(5);
int y = add(true, 5);
int z = add(5, 3);
return z;
}
Type Rules
Variable Declaration
Variables must be declared with a type before use:
int x;
x = 42;
int y = 10;
float pi = 3.14;
bool flag = true;
Variable Initialization
GLaDOS tracks whether variables are initialized before use:
int x;
return x;
int x = 42;
return x;
int y;
y = 10;
return y;
Assignment Type Checking
Assignments must match the variable's declared type:
int x = 5;
x = 10;
x = true;
Expression Type Rules
Every expression has a type determined by its components:
Arithmetic Expressions
Rule: Both operands must be the same numeric type (int or float), result is same type
int a = 5 + 3;
int b = 10 - 2;
int c = 4 * 5;
int d = 20 / 4;
float e = 3.14 + 2.86;
float f = 10.5 - 3.2;
float g = 2.5 * 4.0;
float h = 9.0 / 3.0;
int i = 5 + true;
int j = 5 + 3.14;
float k = 3.14 + 5;
Comparison Expressions
Rule: Both operands must be same type, result is bool
bool a = 5 == 5;
bool b = 5 != 3;
bool c = 5 < 10;
bool d = 3.14 == 3.14;
bool e = 2.5 < 3.7;
bool f = 1.0 >= 0.5;
bool g = true == false;
bool h = 5 == true;
bool i = 3.14 == 5;
bool j = 5 < 3.14;
Logical Expressions
Rule: Both operands must be bool, result is bool
bool a = true && false;
bool b = true || false;
bool c = (5 > 3) && (10 < 20);
bool d = 5 && 10;
bool e = true && 5;
Control Flow Type Rules
If Statement Condition
Rule: Condition must be type bool
if (5 > 3) {
return 1;
}
bool flag = true;
if (flag) {
return 1;
}
if (5) {
return 1;
}
While Loop Condition
Rule: Condition must be type bool
while (x > 0) {
x = x - 1;
}
while (x) {
x = x - 1;
}
Return Statement Type Rules
Rule: Return expression type must match function return type
int getValue() {
return 42;
}
float getPi() {
return 3.14159;
}
bool isReady() {
return true;
}
void doSomething() {
}
int getBad() {
return true;
}
float getWrong() {
return 5;
}
void voidFunc() {
return 42;
}
All Paths Must Return
Rule: Every non-void function code path must return a value of the correct type
int bad(int x) {
if (x > 0) {
return 1;
}
}
int good(int x) {
if (x > 0) {
return 1;
} else {
return 0;
}
}
int alsoGood(int x) {
if (x > 0) {
return 1;
}
return 0;
}
void printValue(int x) {
if (x > 0) {
return;
}
}
Note: The return path analysis only applies to non-void functions. Void functions are not required to have return statements on all paths.
Type Error Messages
The type checker provides detailed error messages:
Type error at line 5: expected int but got bool
Type error at line 8: expected float but got int
Return type mismatch at line 10: expected int but got bool
Return type mismatch at line 15: expected void but got int
Type error in arithmetic at line 7: expected int but got bool
Type error in arithmetic at line 12: cannot mix int and float
Function 'add' expects 2 arguments but got 1 at line 15
Type mismatch in function argument at line 12: expected int but got bool
Type mismatch in function argument at line 18: expected float but got int
Each message includes:
- Error category (Type error, Return type mismatch, etc.)
- Source location (line number)
- Expected type vs Actual type
Type System Implementation
Internal Representation
Types are represented in Haskell as:
data Type
= TypeInt
| TypeFloat
| TypeBool
| TypeVoid
| TypeInfer
deriving (Show, Eq)
Notes:
TypeInferis used internally by the compiler during type checking for intermediate expressions- User code cannot explicitly use the
TypeInfertype - All variable declarations require explicit type annotations
Type Checking Algorithm
The type checker performs:
- Environment Building: Collect all function signatures
- Function Validation: Check each function body
- Expression Type Inference: Determine type of each expression
- Type Constraint Verification: Ensure all operations are type-safe
- Initialization Tracking: Verify variables are initialized before use
- Return Path Analysis: Ensure all paths return correct type
Type Environment
The type checker maintains:
- Global Environment: Function signatures
- Local Environment: Local variable types and initialization status
- Expected Return Type: Current function's return type
Limitations and Design Choices
No User-Level Type Inference
Variables must have explicit type declarations. This is by design for simplicity and clarity.
Note: The compiler internally uses a TypeInfer type during type checking for intermediate expressions, but this is not exposed to user code. All variable and parameter declarations require explicit type annotations.
No Implicit Conversions
No automatic conversions between types (e.g., int to bool, int to float, or vice versa). This prevents common bugs.
Examples of rejected implicit conversions:
int x = 5;
float y = x;
float a = 3.14;
int b = a;
bool flag = 1;
int count = true;
All type conversions must be explicit (note: GLaDOS currently does not provide explicit conversion functions, so values must be literals of the correct type).
No User-Defined Types
GLaDOS does not support:
- Structs or records
- Enums
- Type aliases
- Generic/parametric types
This keeps the language simple and focused.
Void Return Type
Functions can have void return type when they don't return a value:
void helper(int x) {
}
void earlyExit(bool condition) {
if (condition) {
return;
}
}
Important:
- Void can only be used as a function return type
- Void functions don't need explicit return statements
- Can use
return;(without expression) for early exit - Cannot declare void variables or parameters
- Type checker doesn't enforce return path analysis for void functions
No Null or Optional Types
There is no concept of null, undefined, or optional values. All variables must be initialized.
Type Safety Guarantees
GLaDOS's type system guarantees:
- Type Soundness: Well-typed programs cannot have type errors at runtime
- Initialization Safety: Variables cannot be read before being initialized
- Return Safety: Functions always return a value of the correct type
- Operation Safety: Operators are only applied to compatible types
These guarantees eliminate entire classes of bugs at compile time.