Skip to content

ddn.adam

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

Overview

adam is a memory-efficient alternative to ddn.var, using only 16 bytes on 64-bit platforms (vs. 24+ bytes for typical variant types). It provides the same API and semantics as var while minimizing memory footprint.

Basic Value Assignment

Create and assign values of different types:

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

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

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

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

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

    // Characters (normalized to dchar)
    adam letter = 'D';
    adam emoji = '👍';
    writeln("Letter: ", letter);  // Output: D
    writeln("Emoji: ", emoji);    // Output: 👍

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

    // Check memory footprint
    writeln("\nadam.sizeof = ", adam.sizeof, " bytes");  // Output: 16 bytes
}

Type Conversion with get!T

Extract values as specific D types:

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

void main() {
    adam num = 42;

    // Convert to different numeric types
    int i = num.get!int;
    long l = num.get!long;
    double d = num.get!double;
    string s = num.get!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
    adam zero = 0;
    adam nonZero = 5;
    writeln("Zero as bool: ", zero.get!bool);      // Output: false
    writeln("NonZero as bool: ", nonZero.get!bool); // Output: true

    // Type normalization
    adam charVal = 'A';
    writeln("Char type: ", charVal.type);  // Output: DCHAR (normalized)
}

Working with Arrays

Create and manipulate dynamic arrays:

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

void main() {
    // Create an array
    adam arr = [adam(1), adam(2), adam(3)];
    writeln("Array: ", arr);  // Output: [1,2,3]
    writeln("Length: ", arr.length);  // Output: 3

    // Access elements by index
    writeln("First: ", arr[0].get!int);   // Output: 1
    writeln("Last: ", arr[2].get!int);    // Output: 3

    // Modify elements
    arr[0] = 100;
    writeln("Modified: ", arr);  // Output: [100,2,3]

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

    // Iterate over array elements
    adam fruits = [adam("apple"), adam("banana"), adam("cherry")];
    write("Fruits: ");
    foreach (i; 0 .. fruits.length) {
        write(fruits[i].get!string, " ");
    }
    writeln();  // Output: Fruits: apple banana cherry
}

Working with Objects (Maps)

Create and manipulate key-value maps:

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

void main() {
    // Create an object using indexing
    adam 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"].get!string);  // Output: Alice
    writeln("Age: ", person["age"].get!int);       // Output: 30

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

    // Get all keys
    writeln("Keys: ", person.keys());  // Output: ["active", "age", "name"]
}

Nested Structures

Work with complex nested data:

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

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

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

    // Nested array
    adam tags = [adam("tech"), adam("startup"), adam("innovation")];
    company["tags"] = tags;

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

    // Serialize to JSON
    writeln("\nFull structure:");
    writeln(company.toJSON());
}

Arithmetic Operations

Perform arithmetic on numeric values:

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

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

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

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

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

    // Unary operations
    adam n = 42;
    writeln("-n = ", (-n).get!int);  // Output: -42
}

Comparison Operations

Compare adam values:

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

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

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

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

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

JSON Serialization

Convert adam values to JSON strings:

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

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

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

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

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

    // Complex nested structure
    adam config;
    config["server"] = "localhost";
    config["ports"] = [adam(80), adam(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 an adam value:

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

void main() {
    adam[] values = [
        adam.init,           // NULL
        adam(true),          // BOOL
        adam(42),            // INT
        adam(3.14),          // DOUBLE
        adam("text"),        // STRING
        adam([adam(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
    adam data = [adam(1), adam(2), adam(3)];

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

Memory Efficiency Comparison

Demonstrate the memory savings of adam vs var:

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

void main() {
    writeln("Size comparison:");
    writeln("  adam.sizeof = ", adam.sizeof, " bytes");
    writeln("  var.sizeof  = ", var.sizeof, " bytes");

    // For large collections, the savings add up
    enum N = 10_000;

    writeln("\nFor ", N, " elements:");
    writeln("  adam[] would use ~", N * adam.sizeof / 1024, " KB");
    writeln("  var[]  would use ~", N * var.sizeof / 1024, " KB");

    // Both types work the same way
    adam a = 42;
    var v = 42;

    writeln("\nBoth produce same JSON:");
    writeln("  adam: ", a.toJSON());
    writeln("  var:  ", v.toJSON());
}

Practical Example: Compact Data Store

A complete example showing real-world usage for memory-efficient data storage:

/+ dub.sdl:
    name "adam-datastore"
    dependency "ddn" path="../.."
+/
import ddn.adam;
import std.stdio;
import std.array : appender;

/// A simple in-memory data store using adam for compact storage
struct DataStore {
    adam[] records;

    void add(adam record) {
        records ~= record;
    }

    adam[] findByField(string field, adam value) {
        auto results = appender!(adam[]);
        foreach (ref rec; records) {
            if (rec.type == adam.Type.OBJECT && rec.contains(field)) {
                if (rec[field] == value) {
                    results ~= rec;
                }
            }
        }
        return results[];
    }

    string toJSON() {
        adam arr = records;
        return arr.toJSON();
    }
}

void main() {
    DataStore store;

    // Add some records
    adam user1;
    user1["id"] = 1;
    user1["name"] = "Alice";
    user1["role"] = "admin";
    store.add(user1);

    adam user2;
    user2["id"] = 2;
    user2["name"] = "Bob";
    user2["role"] = "user";
    store.add(user2);

    adam user3;
    user3["id"] = 3;
    user3["name"] = "Carol";
    user3["role"] = "user";
    store.add(user3);

    // Query the store
    writeln("All records:");
    writeln(store.toJSON());

    writeln("\nUsers with role 'user':");
    auto users = store.findByField("role", adam("user"));
    foreach (ref u; users) {
        writeln("  - ", u["name"].get!string);
    }

    writeln("\nMemory used: ~", store.records.length * adam.sizeof, " bytes for values");
}

When to Use adam vs var

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

void main() {
    writeln("Use adam when:");
    writeln("  - Memory is constrained");
    writeln("  - Storing large collections of dynamic values");
    writeln("  - Network serialization with size limits");
    writeln("  - Embedded systems or high-density data");

    writeln("\nUse var when:");
    writeln("  - Maximum API compatibility is needed");
    writeln("  - Working with existing var-based code");
    writeln("  - Memory is not a primary concern");

    writeln("\nBoth types:");
    writeln("  - Support the same value types");
    writeln("  - Have compatible JSON serialization");
    writeln("  - Support arrays and objects");
    writeln("  - Are @safe and GC-friendly");
}

See Also