Library Features
Type-Safe Value Access
Section titled “Type-Safe Value Access”CCL values are always strings. Type conversion is a library convenience, not part of Core CCL.
Common Functions:
get_string(config, path...)- Extract string valuesget_int(config, path...)- Parse integers with validationget_bool(config, path...)- Parse booleans (true/false, yes/no, 1/0)get_list(config, path...)- Extract lists from empty-key entries or duplicate keys
Example:
app = name = MyApp port = 8080 debug = truename = get_string(config, "app", "name") // "MyApp"port = get_int(config, "app", "port") // 8080debug = get_bool(config, "app", "debug") // trueEntry Processing
Section titled “Entry Processing”Manipulate CCL entries for composition and filtering.
Common Functions:
filter(entries, predicate)- Remove entries (e.g., comments)compose(entries1, entries2)- Concatenate entry lists before downstream hierarchy building
Example:
/= Development configdatabase = host = localhost
/= Production overridesdatabase = host = prod.db.comdev_entries = parse(dev_config)prod_entries = parse(prod_config)combined = compose(dev_entries, prod_entries)final_config = build_hierarchy(combined)compose is intentionally an entry-processing helper, not an object merge API.
The expected flow is:
entries_a = parse(text_a)entries_b = parse(text_b)merged_entries = compose(entries_a, entries_b)final_config = build_hierarchy(merged_entries)This keeps composition pure at the Entry[] layer and gives [] the expected
left/right identity for compose.
Formatting Functions
Section titled “Formatting Functions”CCL provides two distinct formatting functions that serve different purposes.
| Function | Purpose | Property |
|---|---|---|
print | Standard format | Structure-preserving: print(parse(x)) == x for standard inputs |
canonical_format | Model-level format | Semantic-preserving: transforms key = value to nested form |
The print Function
Section titled “The print Function”Purpose: Convert parsed entries back to CCL text format, preserving the original structure.
Key Property: For inputs in standard format:
print(parse(x)) == xThis is an entry-level isomorphism - the round-trip preserves the textual structure.
Example:
name = Aliceconfig = port = 8080 debug = trueAfter parse and print, the structure is preserved exactly.
The canonical_format Function
Section titled “The canonical_format Function”Purpose: Render a CCL value as normalized CCL text — stable key ordering, consistent indentation, no redundant whitespace. Two semantically equal inputs produce identical output.
Key Property: For a canonical input, canonical_format is a fixed point:
canonical_format(canonical_format(x)) == canonical_format(x)Unlike print, which preserves the original text structure, canonical_format normalizes key order and indentation. The OCaml reference implementation’s pretty function is its equivalent.
Note on the underlying model: The canonical CCL data model represents all values as keys in a recursive map — name = Alice becomes {"name": {"Alice": {}}} internally. canonical_format renders this model back to CCL text. This means two inputs that are semantically equal in the model produce identical canonical_format output, even if they looked different as source text.
Standard Input Format
Section titled “Standard Input Format”A CCL input is in standard format when:
- Keys have exactly one space before and after
= - Nested content uses 2-space indentation per level
- Line endings are LF only (CR characters become part of value content)
- No extra whitespace before keys or after values
Standard format:
key = valuenested = child = valueNon-standard (extra spaces):
key = value nested =Round-Trip Testing
Section titled “Round-Trip Testing”Use round_trip to verify the isomorphism property:
parse(print(parse(x))) == parse(x)This verifies that print followed by parse produces identical entries to the original parse.
Implementation Guidance
Section titled “Implementation Guidance”For structure-preserving print, implementations need to track whether a value was originally a string or nested structure. Options:
- Leaf flag: Mark nodes that were originally string values
- Original value storage: Keep raw string alongside children
- Entry preservation: Keep original entry list, build hierarchy on-demand
For new implementations, use a tagged union type:
type Value = | String(string) | Object(map<string, Value>) | List(list<Value>)This makes print straightforward to implement while still supporting canonical_format when needed.
Experimental Features
Section titled “Experimental Features”Some implementations provide additional experimental features:
Dotted Representation (experimental):
- Allows accessing nested values using dot-separated paths in a single argument
- Example:
get_string(config, "database.host")instead ofget_string(config, "database", "host") - Not recommended for new implementations - use variadic path arguments as the standard API
- May be useful for compatibility with existing configuration conventions
Test Suite Coverage
Section titled “Test Suite Coverage”The CCL Test Suite provides tests for these features:
- Type-Safe Access:
get_string,get_int,get_bool,get_float,get_list— covered by tests taggedoptional_typed_accessors - Entry Processing:
filter,compose, and compose algebraic properties evaluated by normalizingbuild_hierarchy(compose(...)) - Formatting:
printandround_triptests verify isomorphism properties - Experimental Features:
experimental_dotted_keystests for dotted representation
See Test Suite Guide for complete function list and filtering examples.