What is Bag?

GitHub

A Bag is an intermediate representation (IR) that bridges the gap between how humans think about structured data and how software implements it.

The Problem

When you work with configuration files, API responses, form data, or document structures, you’re constantly translating between mental models:

  • A configuration is a tree of settings with names and values

  • An API response is nested data with metadata attached

  • A form is a collection of fields with labels, values, and validation rules

  • A document is hierarchical content with formatting attributes

Each of these has the same fundamental shape: named things containing values, organized in a hierarchy, with additional properties attached.

Yet in code, we scatter this across dictionaries, classes, JSON, XML, database rows—each with its own access patterns, serialization rules, and limitations.

The Bag Abstraction

A Bag unifies these patterns into a single, consistent model:

  • Nodes — Named containers that hold a value

  • Hierarchy — Nodes can contain other nodes, forming a tree

  • Attributes — Each node carries metadata alongside its value

  • Paths — Navigate the tree with familiar dot notation

This abstraction lets you work with structured data the way you think about it, regardless of where it comes from or where it goes.

Real-World Mapping

Concept

In the real world

In a Bag

A setting

“The database host is localhost”

Node with path config.database.host, value localhost

A labeled value

“User: Alice (admin)”

Node user with value Alice, attribute role=admin

A form field

“Email field, required, must be valid”

Node with value, attributes for validation rules

Nested structure

“The server has connection settings”

Parent node containing child nodes

Why Not Just Use Dictionaries?

Dictionaries are powerful but low-level. They don’t provide:

  • Path-based accessd['a']['b']['c'] vs bag['a.b.c']

  • Attributes on values — You can’t attach metadata to d['key']

  • Ordered iteration — Dict order isn’t always guaranteed or meaningful

  • Change notification — No built-in way to react when values change

  • Type-agnostic serialization — You handle JSON, XML, YAML separately

A Bag wraps these concerns into a coherent abstraction.

Why Not Just Use Classes?

Classes bind structure to behavior. A Bag separates them:

  • Dynamic structure — Shape isn’t fixed at definition time

  • Uniform access — Same API regardless of content

  • Serialization — Round-trips to XML, JSON, MessagePack without boilerplate

  • Introspection — Walk the tree, query attributes, transform at runtime

Use classes when you need fixed contracts. Use Bags when structure emerges from data.

The Layered Design

Bag provides progressive capability through optional layers:

  1. Core Bag — The fundamental container with paths, values, attributes

  2. Resolvers — Values that compute themselves (lazy loading, API calls)

  3. Subscriptions — React to changes (validation, logging, sync)

Start with core Bag. Add layers only when you need them.

Where Bag Fits

Bag is not a database, not a schema validator, not a framework. It’s a data structure that sits between:

  • Raw data sources (files, APIs, user input)

  • Your application logic

  • Output formats (HTML, XML, serialized storage)

It provides a consistent, navigable, observable tree of named values—nothing more, nothing less.


Next: Getting Started — Learn the three core concepts in 5 minutes