Skip to content

Type System & Safety

Harneet features a robust and comprehensive type system designed to ensure code correctness and prevent runtime errors through static analysis.

Comprehensive Type Checking

  • Static Type Analysis: Harneet performs complete type checking during the parsing phase, catching type-related errors before runtime.

    1
    2
    3
    4
    ```harneet
    // Example: Type mismatch caught at compile time
    var num int = "hello" // Error: Cannot assign string to int
    ```
    
  • Function Parameter Validation: Automatic type checking ensures that function calls provide arguments matching the declared parameter types, with detailed error messages for mismatches.

    1
    2
    3
    4
    5
    6
    ```harneet
    function add(a int, b int) int {
        return a + b
    }
    var result = add(10, "hello") // Error: Argument 'hello' (string) cannot be assigned to parameter 'b' (int)
    ```
    
  • Variable Type Declarations: Explicit type annotations (e.g., var x int = 42) allow for compile-time validation, ensuring type consistency.

    1
    2
    3
    4
    ```harneet
    var age int = 30
    // var age string = "thirty" // Error: Cannot re-declare 'age' with a different type
    ```
    
  • Return Type Validation: Functions must return values that match their declared return types.

    1
    2
    3
    4
    5
    ```harneet
    function getNumber() int {
        return "hello" // Error: Function declared to return int, but returning string
    }
    ```
    
  • Array Type Consistency: Arrays can be type-safe (e.g., int[], string[], bool[]), with validation of element types.

    1
    2
    3
    4
    ```harneet
    var numbers = [1, 2, 3] // Inferred as int[]
    // numbers = [1, "hello"] // Error: Cannot assign string to int[] array
    ```
    
  • Advanced Type Inference: The language features sophisticated type inference for expressions, arrays, and function calls, reducing the need for explicit type declarations.

    1
    2
    3
    4
    5
    ```harneet
    var inferredNum = 10          // inferred as int
    var inferredStr = "Harneet"   // inferred as string
    var inferredBool = true       // inferred as bool
    ```
    
  • Type Compatibility: Smart type compatibility rules are applied for numeric types and safe conversions.

    1
    2
    3
    4
    5
    ```harneet
    var intVal int = 10
    var floatVal float64 = intVal // int can be assigned to float64
    // var anotherInt int = floatVal // Error: float64 cannot be assigned to int without explicit conversion
    ```
    
  • Enhanced Error Messages: Detailed error messages provide helpful suggestions and precise locations for type-related issues.

    1
    2
    3
    4
    5
    6
    ```harneet
    // Example of an enhanced error message
    // Calling a function with wrong argument type might produce:
    // Error: Argument 'hello' (string) cannot be assigned to parameter 'b' (int) in function 'add' at line 5, column 12.
    // Suggestion: Ensure argument type matches parameter type.
    ```
    
  • Zero Runtime Type Errors: Static analysis aims to prevent type-related errors from occurring at runtime.

    1
    2
    3
    4
    ```harneet
    // Because of static analysis, code like this will not even run:
    // var x int = "abc" // Caught at compile time, preventing runtime crash
    ```
    
  • Zero Value Type Safety: All declared variables automatically receive appropriate zero values for their respective types, ensuring predictable behavior.

    1
    2
    3
    4
    5
    ```harneet
    var defaultInt int       // defaultInt is 0
    var defaultString string // defaultString is ""
    var defaultBool bool     // defaultBool is false
    ```
    

Development Features

The type system is complemented by several development features that aid in debugging and code quality.

  • REPL Support: The interactive Read-Eval-Print Loop (REPL) provides immediate feedback on type correctness during development.
  • Debug Mode: A comprehensive logging system with a -debug flag helps in tracing type-related issues.
  • Stack Traces: Detailed error traces with source locations provide context for type errors.
  • Import Analysis: The system detects unused and duplicate imports, promoting cleaner code.
  • Error Suggestions: Helpful error messages often include suggestions for fixing type-related problems.

The any Type

Harneet supports the any type as a top type (supertype of all types) with strict, one-way assignability rules. This design preserves Harneet's strong typing while enabling flexibility at dynamic boundaries like JSON, OS, regex, and external APIs.

Strict Assignability Rules

The any type follows these strict rules:

  • T → any (Upcast): ✅ ALLOWED - Any concrete type can be assigned to any
  • any → T (Downcast): ❌ FORBIDDEN - Requires explicit cast using the cast module

This one-way assignability prevents type safety violations while allowing flexibility where needed.

Upcast to Any
// ✅ Upcast to any - ALLOWED
var anyInt any = 42
var anyString any = "hello"
var anyMap any = {"key": "value"}

// ❌ Downcast without cast - FORBIDDEN
var x any = 42
var num int = x  // COMPILE ERROR: cannot assign any to int

// ✅ Explicit cast - REQUIRED for downcast
var num, err = cast.ToInt(x)  // OK
if err == None {
    fmt.Printf("Number: %d\n", num)
}

When to Use any

✅ Good Use Cases:

  1. JSON Parsing - Dynamic JSON data

    var data, _ = json.Unmarshal(jsonString)
    var userMap, _ = cast.ToMap(data)
    

  2. External APIs - Heterogeneous system data

    1
    2
    3
    4
    function processAPIResponse(response any) {
        var data, _ = cast.ToMap(response)
        // Process typed data...
    }
    

  3. Configuration - Dynamic config values

    1
    2
    3
    4
    5
    var config = {
        "port": 8080,
        "host": "localhost",
        "debug": true
    }
    

  4. Generic Utilities - Flexible helper functions

    1
    2
    3
    function logValue(key string, value any) {
        fmt.Printf("%s: %v\n", key, value)
    }
    

❌ Avoid Using any:

  1. Internal business logic - Use precise types
  2. Public APIs - Prefer concrete types for clarity
  3. Data structures - Use typed collections

Explicit Narrowing

The cast module provides type-safe narrowing from any to concrete types:

Safe Type Narrowing
package main
import fmt
import cast

var mystery any = "12345"

// Method 1: Direct conversion
var num, err = cast.ToInt(mystery)
if err == None {
    fmt.Printf("Converted: %d\n", num)
}

// Method 2: Preflight check
var canConvert, _ = cast.CanCast(mystery, "int")
if canConvert {
    var num, _ = cast.ToInt(mystery)
    // Safe to use num
}

Available Cast Functions: - cast.ToInt(any) - Convert to integer - cast.ToString(any) - Convert to string - cast.ToBool(any) - Convert to boolean - cast.ToFloat(any) - Convert to float - cast.ToArray(any) - Convert to array - cast.ToMap(any) - Convert to map - cast.CanCast(any, type) - Check if conversion is possible

Best Practices

Early Narrowing Pattern (Recommended):

Early Narrowing Pattern
package main
import fmt
import cast

// Good: Narrow at boundary, keep core typed
function processConfig(config any) {
    // Narrow immediately
    var configMap, err = cast.ToMap(config)
    if err != None {
        fmt.Println("Error: config must be a map")
        return
    }

    // Now work with typed data
    var port, _ = cast.ToInt(configMap["port"])
    var host, _ = cast.ToString(configMap["host"])

    // Core logic uses concrete types
    startServer(host, port)
}

Type Guard Pattern:

Type Guard Pattern
package main
import fmt
import cast
function handleDynamic(val any) {
    // Try each type in order
    var asInt, intErr = cast.ToInt(val)
    if intErr == None {
        handleInteger(asInt)
        return
    }

    var asString, strErr = cast.ToString(val)
    if strErr == None {
        handleString(asString)
        return
    }

    fmt.Println("Unsupported type")
}

Type Safety Guarantees

The any type maintains Harneet's strong type safety through:

  1. Compile-Time Enforcement - Cannot assign any to concrete types without explicit cast
  2. Runtime Validation - Cast functions validate conversions and return errors
  3. No Implicit Conversions - All narrowing must be explicit
  4. Clear Error Messages - Detailed feedback when conversions fail
Error Messages
1
2
3
var x any = "not a number"
var y, err = cast.ToInt(x)
// err contains: "cannot convert string \"not a number\" to integer"

Zero Values System

Harneet automatically initializes all declared variables to their "zero value" if no explicit value is provided. This ensures that variables always have a defined state.

Zero Value Initialization
// All integer types initialize to 0
var intVal int           // 0
var int8Val int8         // 0
var int16Val int16       // 0
var int32Val int32       // 0
var int64Val int64       // 0
var uintVal uint         // 0
var uint8Val uint8       // 0
var uint16Val uint16     // 0
var uint32Val uint32     // 0
var uint64Val uint64     // 0
var uintptrVal uintptr   // 0

// Float types initialize to 0.0
var float32Val float32   // 0.0
var float64Val float64   // 0.0

// String type initializes to empty string
var stringVal string     // ""

// Boolean type initializes to false
var boolVal bool         // false

// The unit type initializes to its single value
var unitVal unit         // unit

// Mixed zero value and explicit assignments
var zeroInt int          // 0 (zero value)
var assignedInt int = 42 // 42 (explicit value)

Multiple Assignment Examples

The type system works seamlessly with multiple assignment, ensuring type safety even when multiple values are returned or assigned.

Multiple Assignment
package main
import math
import strings

// Function returns (traditional tuple assignment)
var result, err = math.Abs(-42)
var upper, upperErr = strings.Upper("hello")

// Multiple expressions with explicit types
var name string, age int, score float64 = "Alice", 25, 98.5
var x int, y int, z int = 10, 20, 30

// Mixed explicit and inferred types
var first string, second, third int = "test", 42, 100

// Multiple assignment with explicit types
var a string, b int, c bool = "hello", 123, true
var xf float64, yf float64, zf float64 = 1.0, 2.0, 3.0

// Blank identifier support
var value, _ = math.Max(10, 5)  // Discard error
var _, important, _ = "ignore", "keep", "ignore"