Skip to content

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.

Quick Start

Any Type Quick Start
package main
import fmt
import cast

// ✅ Upcast to any - ALLOWED
var x any = 42
var y any = "hello"

fmt.Println(x)  // Prints: 42

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

Core Principles

Strict Assignability Rules

The any type follows these fundamental rules:

Direction Allowed? Description
T → any YES Any concrete type can be assigned to any (upcast)
any → T NO Cannot assign any to concrete type without explicit cast (downcast)

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

Basic Usage

Variable Declaration

Any Variable Declaration
1
2
3
4
5
6
7
8
9
// Declare any variables - accepts all types
var dynamic any = 100
var flexible any = "can be anything"

// Any type accepts all values
dynamic = "now a string"
dynamic = true
dynamic = [1, 2, 3]
dynamic = {"name": "Alice"}

Function Parameters

Any Function Parameters
package main
import fmt

// Function accepting any type
function printValue(val any) {
    fmt.Printf("Value: %v\n", val)
}

// Works with all types
printValue(42)
printValue("hello")
printValue(true)
printValue([1, 2, 3])
printValue({"key": "value"})

Function Returns

Any Function Returns
package main
import fmt
import cast

// Function returning any
function getDynamic(flag bool) any {
    if flag {
        return 42
    } else {
        return "forty-two"
    }
}

// Must narrow with explicit cast
var result = getDynamic(true)
var num, err = cast.ToInt(result)
if err == None {
    fmt.Printf("Number: %d\n", num)
}

Explicit Narrowing

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

Available Cast Functions

Function Purpose Example
cast.ToInt(any) Convert to integer var n, err = cast.ToInt(x)
cast.ToString(any) Convert to string var s, err = cast.ToString(x)
cast.ToBool(any) Convert to boolean var b, err = cast.ToBool(x)
cast.ToFloat(any) Convert to float var f, err = cast.ToFloat(x)
cast.ToArray(any) Convert to array var a, err = cast.ToArray(x)
cast.ToMap(any) Convert to map var m, err = cast.ToMap(x)
cast.CanCast(any, type) Check if possible var ok, _ = cast.CanCast(x, "int")

Narrowing Examples

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)
    fmt.Printf("Safe to use: %d\n", num)
}

Best Practices

✅ When to Use any

Use any at dynamic boundaries:

1. JSON Parsing

JSON Parsing
import json
import cast

var jsonStr = "{\"name\": \"Alice\", \"age\": 25}"
var data, _ = json.Unmarshal(jsonStr)

// Narrow immediately
var obj, _ = cast.ToMap(data)
var name, _ = cast.ToString(obj["name"])
var age, _ = cast.ToInt(obj["age"])

2. External APIs

External APIs
package main
import fmt
function processAPIResponse(response any) {
    var data, err = cast.ToMap(response)
    if err != None {
        fmt.Println("Invalid response format")
        return
    }
    // Process typed data...
}

3. Configuration Management

Configuration Management
var config = {
    "port": 8080,
    "host": "localhost",
    "debug": true,
    "timeout": 30.5
}

function getConfigValue(key string) any {
    return config[key]
}

// Narrow when retrieving
var port, _ = cast.ToInt(getConfigValue("port"))

4. Generic Utilities

Generic Utilities
1
2
3
4
5
6
7
8
9
package main
import fmt
function logValue(key string, value any) {
    fmt.Printf("%s: %v\n", key, value)
}

logValue("count", 42)
logValue("name", "Alice")
logValue("active", true)

❌ When to Avoid any

Don't use any for:

  1. Internal Business Logic

    1
    2
    3
    4
    5
    // ❌ Bad - loses type safety
    function calculateTotal(items any) any { ... }
    
    // ✅ Good - maintains type safety
    function calculateTotal(items []Item) float64 { ... }
    

  2. Public APIs

    1
    2
    3
    4
    5
    // ❌ Bad - unclear contract
    function createUser(data any) any { ... }
    
    // ✅ Good - clear contract
    function createUser(name string, age int) User { ... }
    

  3. Data Structures

    1
    2
    3
    4
    5
    // ❌ Bad - no type safety
    var users []any
    
    // ✅ Good - typed collection
    var users []User
    

Design Patterns

Pattern 1: Early Narrowing

Narrow at the boundary, keep core typed:

Early Narrowing
package main
import fmt
import cast

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

    // Extract and narrow individual fields
    var port, _ = cast.ToInt(configMap["port"])
    var host, _ = cast.ToString(configMap["host"])
    var debug, _ = cast.ToBool(configMap["debug"])

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

Pattern 2: Type Guards

Try each type in order:

Type Guard Pattern
package main
import fmt
import cast

function handleDynamic(val any) {
    // Try integer first
    var asInt, intErr = cast.ToInt(val)
    if intErr == None {
        fmt.Printf("Handling integer: %d\n", asInt)
        return
    }

    // Try string next
    var asString, strErr = cast.ToString(val)
    if strErr == None {
        fmt.Printf("Handling string: %s\n", asString)
        return
    }

    // Try boolean
    var asBool, boolErr = cast.ToBool(val)
    if boolErr == None {
        fmt.Printf("Handling boolean: %v\n", asBool)
        return
    }

    fmt.Println("Unsupported type")
}

Pattern 3: API Response Processing

Process dynamic JSON data:

Dynamic JSON Processing
import json
import cast

function fetchUserData(apiResponse string) {
    // Parse JSON (returns any)
    var data, err = json.Unmarshal(apiResponse)
    if err != None {
        return
    }

    // Narrow to map
    var userMap, mapErr = cast.ToMap(data)
    if mapErr != None {
        return
    }

    // Extract and narrow fields
    var id, _ = cast.ToInt(userMap["id"])
    var name, _ = cast.ToString(userMap["name"])
    var email, _ = cast.ToString(userMap["email"])
    var active, _ = cast.ToBool(userMap["active"])

    // Use typed data in core logic
    processUser(id, name, email, active)
}

Type Safety Guarantees

Compile-Time Enforcement

Compile-Time Safety
1
2
3
4
5
var x any = 42
var y int = x  // ❌ COMPILE ERROR: cannot assign any to int

// Must use explicit cast
var y, err = cast.ToInt(x)  // ✅ OK

Runtime Validation

Runtime Validation
1
2
3
4
5
6
7
8
9
package main
import fmt
var x any = "not a number"
var y, err = cast.ToInt(x)  // ✅ Returns error, doesn't panic

if err != None {
    fmt.Println(err)
    // Output: cast.ToInt: cannot convert string "not a number" to integer
}

No Implicit Conversions

No Implicit Conversions
1
2
3
4
5
6
7
8
9
var config any = {"port": "8080"}
var configMap, _ = cast.ToMap(config)

// ❌ This would fail - port is string, not int
var port, err = cast.ToInt(configMap["port"])

// ✅ This works - convert string to int
var portStr, _ = cast.ToString(configMap["port"])
var port, err = cast.ToInt(portStr)

Real-World Examples

Example 1: Configuration System

Configuration System
package main
import fmt
import cast
import os

function loadConfig() {
    var config = {
        "port": 8080,
        "host": "0.0.0.0",
        "debug": false,
        "timeout": 30.5
    }

    // Override from environment
    var portEnv, _ = os.Getenv("PORT")
    if portEnv != "" {
        var port, err = cast.ToInt(portEnv)
        if err == None {
            config["port"] = port
        }
    }

    return config
}

function startApp() {
    var cfg = loadConfig()

    // Narrow configuration values
    var port, _ = cast.ToInt(cfg["port"])
    var host, _ = cast.ToString(cfg["host"])
    var debug, _ = cast.ToBool(cfg["debug"])

    fmt.Printf("Starting server on %s:%d (debug: %v)\n", host, port, debug)
}

Example 2: Dynamic Command Handler

Command Handler
package main
import fmt
import cast

function executeCommand(cmd string, args any) {
    if cmd == "add" {
        var nums, err = cast.ToArray(args)
        if err != None {
            fmt.Println("add command requires array of numbers")
            return
        }

        var sum = 0
        for num in nums {
            var n, _ = cast.ToInt(num)
            sum = sum + n
        }
        fmt.Printf("Sum: %d\n", sum)

    } else if cmd == "greet" {
        var name, err = cast.ToString(args)
        if err != None {
            fmt.Println("greet command requires string name")
            return
        }
        fmt.Printf("Hello, %s!\n", name)

    } else if cmd == "info" {
        var data, err = cast.ToMap(args)
        if err != None {
            fmt.Println("info command requires map")
            return
        }

        for key, value in data {
            fmt.Printf("%s: %v\n", key, value)
        }
    }
}

// Usage
executeCommand("add", [1, 2, 3, 4, 5])
executeCommand("greet", "Alice")
executeCommand("info", {"version": "1.0", "author": "Harneet"})

Example 3: JSON API Client

JSON API Client
package main
import fmt
import json
import cast
import http

function fetchUser(userId int) {
    var url = fmt.Sprintf("https://api.example.com/users/%d", userId)
    var response, _ = http.Get(url)

    // Parse JSON response
    var data, err = json.Unmarshal(response.Body)
    if err != None {
        fmt.Println("Failed to parse response")
        return
    }

    // Narrow to map
    var userMap, _ = cast.ToMap(data)

    // Extract user fields
    var id, _ = cast.ToInt(userMap["id"])
    var name, _ = cast.ToString(userMap["name"])
    var email, _ = cast.ToString(userMap["email"])
    var age, _ = cast.ToInt(userMap["age"])

    // Use typed data
    fmt.Printf("User %d: %s <%s> (age: %d)\n", id, name, email, age)
}

Comparison with Other Languages

Language Type Behavior
Harneet any Top type Strict: requires explicit cast for downcast
TypeScript any Top type Permissive: allows implicit conversions
Go interface{} Empty interface Requires type assertion
Python Any Type hint Runtime only, no compile-time enforcement
Java Object Root class Requires explicit cast

Harneet's approach: - ✅ Stricter than TypeScript (better safety) - ✅ Cleaner syntax than Go (no type assertions) - ✅ Compile-time enforcement unlike Python - ✅ Integrated cast module (easier than Java)

Summary

The any type in Harneet provides:

Flexibility - Handle dynamic data at boundaries
Type Safety - Explicit conversions required
Clarity - Clear upcast/downcast rules
Integration - Works seamlessly with cast module
Best Practices - Documented patterns
No Surprises - Predictable behavior

Remember: Use any at boundaries (JSON, OS, APIs), but keep core logic typed!

See Also