Skip to content

ddn.var

Practical examples demonstrating everyday usage of the ddn.var dynamic value type.

Basic Value Assignment

Create and assign values of different types to var:

/+ dub.sdl:
    name "var-basic"
    dependency "ddn" path="../.."
+/
import ddn.var;
import std.stdio;

void main() {
    // Null value (default)
    var empty;
    writeln("Empty: ", empty);  // Output: null

    // Boolean
    var flag = true;
    writeln("Flag: ", flag);  // Output: true

    // Integers
    var count = 42;
    var bigNum = 9_223_372_036_854_775_807L;  // long.max
    writeln("Count: ", count);    // Output: 42
    writeln("BigNum: ", bigNum);  // Output: 9223372036854775807

    // Floating point
    var pi = 3.14159;
    var temperature = -40.0f;
    writeln("Pi: ", pi);           // Output: 3.14159
    writeln("Temp: ", temperature); // Output: -40

    // Characters
    var letter = 'D';
    var emoji = '👍';
    writeln("Letter: ", letter);  // Output: D
    writeln("Emoji: ", emoji);    // Output: 👍

    // String
    var greeting = "Hello, World!";
    writeln("Greeting: ", greeting);  // Output: "Hello, World!"
}

Type Conversion with as!T

Extract values as specific D types:

/+ dub.sdl:
    name "var-conversion"
    dependency "ddn" path="../.."
+/
import ddn.var;
import std.stdio;

void main() {
    var num = 42;

    // Convert to different numeric types
    int i = num.as!int;
    long l = num.as!long;
    double d = num.as!double;
    string s = num.as!string;

    writeln("As int: ", i);      // Output: 42
    writeln("As long: ", l);     // Output: 42
    writeln("As double: ", d);   // Output: 42
    writeln("As string: ", s);   // Output: 42

    // Boolean conversion
    var zero = 0;
    var nonZero = 5;
    writeln("Zero as bool: ", zero.as!bool);      // Output: false
    writeln("NonZero as bool: ", nonZero.as!bool); // Output: true

    // String to numeric (when parsing)
    var strNum = "123";
    writeln("String value: ", strNum.as!string);  // Output: 123
}

Working with Arrays

Create and manipulate dynamic arrays:

/+ dub.sdl:
    name "var-arrays"
    dependency "ddn" path="../.."
+/
import ddn.var;
import std.stdio;

void main() {
    // Create an array from D array literal
    var numbers = [1, 2, 3, 4, 5];
    writeln("Numbers: ", numbers);  // Output: [1,2,3,4,5]
    writeln("Length: ", numbers.length);  // Output: 5

    // Access elements by index
    writeln("First: ", numbers[0].as!int);   // Output: 1
    writeln("Last: ", numbers[4].as!int);    // Output: 5

    // Modify elements
    numbers[0].opAssign(100);
    writeln("Modified: ", numbers);  // Output: [100,2,3,4,5]

    // Mixed-type arrays
    var mixed = var([var(1), var("two"), var(3.0), var(true)]);
    writeln("Mixed: ", mixed);  // Output: [1,"two",3,true]

    // Iterate over array elements
    var fruits = var([var("apple"), var("banana"), var("cherry")]);
    write("Fruits: ");
    foreach (ref el; fruits.as!(var[])) {
        write(el.as!string, " ");
    }
    writeln();  // Output: Fruits: apple banana cherry
}

Working with Objects (Maps)

Create and manipulate key-value maps:

/+ dub.sdl:
    name "var-objects"
    dependency "ddn" path="../.."
+/
import ddn.var;
import std.stdio;

void main() {
    // Create an object using indexing
    var person;
    person["name"] = "Alice";
    person["age"] = 30;
    person["active"] = true;

    writeln("Person: ", person);
    // Output: {"active":true,"age":30,"name":"Alice"}

    // Access values
    writeln("Name: ", person["name"].as!string);  // Output: Alice
    writeln("Age: ", person["age"].as!int);       // Output: 30

    // Use opDispatch for field-like access
    var config;
    config.host = "localhost";
    config.port = 8080;
    config.debug_ = true;  // Use underscore for reserved words

    writeln("Host: ", config.host.as!string);  // Output: localhost
    writeln("Port: ", config.port.as!int);     // Output: 8080

    // Check if key exists
    writeln("Has 'name': ", person.contains("name"));   // Output: true
    writeln("Has 'email': ", person.contains("email")); // Output: false

    // Get with default value
    var email = person.get("email", var("unknown@example.com"));
    writeln("Email: ", email.as!string);  // Output: unknown@example.com

    // Get all keys and values
    writeln("Keys: ", person.keys());    // Output: ["active", "age", "name"]
    writeln("Values: ", person.values()); // Output: [true, 30, "Alice"]
}

Nested Structures

Work with complex nested data:

/+ dub.sdl:
    name "var-nested"
    dependency "ddn" path="../.."
+/
import ddn.var;
import std.stdio;

void main() {
    // Build a nested structure
    var company;
    company["name"] = "TechCorp";
    company["founded"] = 2020;

    // Nested object
    var address;
    address["street"] = "123 Main St";
    address["city"] = "Springfield";
    address["zip"] = "12345";
    company["address"] = address;

    // Nested array of objects
    var employees = var([var.init, var.init]);
    employees[0]["name"] = "Bob";
    employees[0]["role"] = "Developer";
    employees[1]["name"] = "Carol";
    employees[1]["role"] = "Designer";
    company["employees"] = employees;

    // Access nested data
    writeln("Company: ", company["name"].as!string);
    writeln("City: ", company["address"]["city"].as!string);
    writeln("First employee: ", company["employees"][0]["name"].as!string);

    // Pretty-print as JSON
    writeln("\nFull structure:");
    writeln(company.toJSON());
}

Arithmetic Operations

Perform arithmetic on numeric values:

/+ dub.sdl:
    name "var-arithmetic"
    dependency "ddn" path="../.."
+/
import ddn.var;
import std.stdio;

void main() {
    var a = 10;
    var b = 3;

    // Basic operations
    writeln("a + b = ", (a + b).as!int);  // Output: 13
    writeln("a - b = ", (a - b).as!int);  // Output: 7
    writeln("a * b = ", (a * b).as!int);  // Output: 30
    writeln("a / b = ", (a / b).as!int);  // Output: 3
    writeln("a % b = ", (a % b).as!int);  // Output: 1

    // Floating point arithmetic
    var x = 10.0;
    var y = 3.0;
    writeln("x / y = ", (x / y).as!double);  // Output: 3.33333...

    // Mixed integer/float promotes to float
    var intVal = 5;
    var floatVal = 2.5;
    writeln("int + float = ", (intVal + floatVal).as!double);  // Output: 7.5

    // Division by zero returns NULL (safe behavior)
    var zero = 0;
    var result = a / zero;
    writeln("10 / 0 type: ", result.type);  // Output: NULL

    // Unary operations
    var n = 42;
    writeln("-n = ", (-n).as!int);  // Output: -42
    writeln("+n = ", (+n).as!int);  // Output: 42
}

Comparison Operations

Compare var values:

/+ dub.sdl:
    name "var-comparison"
    dependency "ddn" path="../.."
+/
import ddn.var;
import std.stdio;

void main() {
    var a = 10;
    var b = 20;
    var c = 10;

    // Equality
    writeln("a == c: ", a == c);  // Output: true
    writeln("a == b: ", a == b);  // Output: false
    writeln("a != b: ", a != b);  // Output: true

    // Ordering
    writeln("a < b: ", a < b);    // Output: true
    writeln("a <= c: ", a <= c);  // Output: true
    writeln("b > a: ", b > a);    // Output: true

    // String comparison
    var s1 = "apple";
    var s2 = "banana";
    writeln("apple < banana: ", s1 < s2);  // Output: true

    // Type-aware comparison
    var intTen = 10;
    var doubleTen = 10.0;
    writeln("int 10 == double 10.0: ", intTen == doubleTen);  // Output: true
}

Deep Copy with dup()

Understand aliasing and create independent copies:

/+ dub.sdl:
    name "var-deepcopy"
    dependency "ddn" path="../.."
+/
import ddn.var;
import std.stdio;

void main() {
    // Assignment aliases arrays and objects
    var original = var([1, 2, 3]);
    var alias_ = original;

    alias_[0].opAssign(999);
    writeln("Original after alias modification: ", original);
    // Output: [999,2,3] - both point to same data!

    // Use dup() for independent copy
    var source = var([10, 20, 30]);
    var copy = source.dup;

    copy[0].opAssign(999);
    writeln("Source after copy modification: ", source);  // Output: [10,20,30]
    writeln("Copy: ", copy);  // Output: [999,20,30]

    // Deep copy works for nested structures too
    var nested;
    nested["data"] = var([1, 2, 3]);
    var nestedCopy = nested.dup;

    nestedCopy["data"][0].opAssign(100);
    writeln("Original nested: ", nested["data"]);      // Output: [1,2,3]
    writeln("Copied nested: ", nestedCopy["data"]);    // Output: [100,2,3]
}

JSON Serialization

Convert var values to JSON strings:

/+ dub.sdl:
    name "var-json"
    dependency "ddn" path="../.."
+/
import ddn.var;
import std.stdio;

void main() {
    // Simple values
    var num = 42;
    var str = "hello";
    var flag = true;

    writeln(num.toJSON());   // Output: 42
    writeln(str.toJSON());   // Output: "hello"
    writeln(flag.toJSON());  // Output: true

    // Arrays
    var arr = var([var(1), var(2), var(3)]);
    writeln(arr.toJSON());  // Output: [1,2,3]

    // Objects (keys are sorted alphabetically)
    var obj;
    obj["zebra"] = 1;
    obj["apple"] = 2;
    obj["mango"] = 3;
    writeln(obj.toJSON());  // Output: {"apple":2,"mango":3,"zebra":1}

    // Complex nested structure
    var config;
    config["server"] = "localhost";
    config["ports"] = var([var(80), var(443)]);
    config["ssl"] = true;

    writeln("\nConfig JSON:");
    writeln(config.toJSON());
    // Output: {"ports":[80,443],"server":"localhost","ssl":true}
}

Type Checking

Inspect the runtime type of a var:

/+ dub.sdl:
    name "var-types"
    dependency "ddn" path="../.."
+/
import ddn.var;
import std.stdio;

void main() {
    var values = [
        var.init,           // NULL
        var(true),          // BOOL
        var(42),            // INT
        var(3.14),          // DOUBLE
        var("text"),        // STRING
        var([var(1)]),      // ARRAY
    ];

    string[] names = ["null", "bool", "int", "double", "string", "array"];

    foreach (i, v; values) {
        writefln("%s: type = %s", names[i], v.type);
    }
    // Output:
    // null: type = NULL
    // bool: type = BOOL
    // int: type = INT
    // double: type = DOUBLE
    // string: type = STRING
    // array: type = ARRAY

    // Type-based dispatch
    var data = var([var(1), var(2), var(3)]);

    if (data.type == var.Type.ARRAY) {
        writeln("Processing array with ", data.length, " elements");
    } else if (data.type == var.Type.OBJECT) {
        writeln("Processing object with ", data.length, " keys");
    } else {
        writeln("Processing scalar value");
    }
}

Error Handling Patterns

Handle edge cases gracefully:

/+ dub.sdl:
    name "var-errors"
    dependency "ddn" path="../.."
+/
import ddn.var;
import std.stdio;

void main() {
    // Safe access with get() and default values
    var config;
    config["host"] = "localhost";

    // Key exists
    writeln("Host: ", config.get("host", var("default")).as!string);
    // Output: localhost

    // Key doesn't exist - returns default
    writeln("Port: ", config.get("port", var(8080)).as!int);
    // Output: 8080

    // Check before access
    if (config.contains("timeout")) {
        writeln("Timeout: ", config["timeout"].as!int);
    } else {
        writeln("Timeout not configured, using default");
    }

    // Division by zero returns NULL instead of crashing
    var a = 100;
    var b = 0;
    var result = a / b;

    if (result.type == var.Type.NULL) {
        writeln("Division by zero detected!");
    }

    // Safe type conversion - incompatible types return T.init
    var text = "not a number";
    int num = text.as!int;  // Returns 0 (int.init)
    writeln("Text as int: ", num);  // Output: 0
}

Practical Example: Configuration Parser

A complete example showing real-world usage:

/+ dub.sdl:
    name "var-config"
    dependency "ddn" path="../.."
+/
import ddn.var;
import std.stdio;

struct AppConfig {
    string host;
    int port;
    bool debug_;
    string[] allowedOrigins;
}

AppConfig parseConfig(var config) {
    AppConfig result;

    result.host = config.get("host", var("localhost")).as!string;
    result.port = config.get("port", var(8080)).as!int;
    result.debug_ = config.get("debug", var(false)).as!bool;

    // Parse array of strings
    if (config.contains("allowedOrigins")) {
        auto origins = config["allowedOrigins"].as!(var[]);
        foreach (ref o; origins) {
            result.allowedOrigins ~= o.as!string;
        }
    }

    return result;
}

void main() {
    // Simulate configuration data
    var config;
    config["host"] = "api.example.com";
    config["port"] = 443;
    config["debug"] = true;
    config["allowedOrigins"] = var([
        var("https://example.com"),
        var("https://app.example.com")
    ]);

    auto appConfig = parseConfig(config);

    writeln("Server Configuration:");
    writeln("  Host: ", appConfig.host);
    writeln("  Port: ", appConfig.port);
    writeln("  Debug: ", appConfig.debug_);
    writeln("  Allowed Origins:");
    foreach (origin; appConfig.allowedOrigins) {
        writeln("    - ", origin);
    }
}

See Also