How does the Dict type work in EK9?

← Getting Started · Ref: Q46

EK9 has a generic Dict type for key-value mappings. Dicts use curly brace literal syntax {key: value} and the generic declaration Dict of (K, V).

DICT CREATION

Three ways to create dicts:

1. Literal syntax (compiler infers the types):

  ages <- {"Alice": 30, "Bob": 25, "Charlie": 35}

The compiler infers Dict of (String, Integer) from the literal contents.

2. Empty typed dict:

  scores <- Dict() of (String, Integer)

You must specify both key and value types when creating an empty dict because there are no entries to infer from.

3. Single-entry construction:

  single <- Dict("Alice", 30)

Creates a Dict of (String, Integer) with one entry.

TWO-PARAMETER GENERIC SYNTAX

Dict requires two type parameters in parentheses: Dict of (K, V). Compare with List which uses a single parameter: List of T. The parentheses are required for multi-parameter generics.

DICTENTRY BASICS

DictEntry of (K, V) represents a single key-value pair. Create with DictEntry(key, value). Access the key with .key() and the value with .value(). DictEntry is the unit of insertion and is yielded when iterating over a Dict.

ADDING ENTRIES

+= mutates the dict in place:

  ages += DictEntry("Dave", 28)

+ creates a new dict (original unchanged):

  moreAges <- ages + DictEntry("Eve", 32)

REMOVING ENTRIES

-= removes by key:

  ages -= "Bob"

SAFE VALUE ACCESS

.getOrDefault(key, default) returns the default if the key is missing:

  aliceAge <- ages.getOrDefault("Alice", 0)       returns 30
  missing <- ages.getOrDefault("Unknown", 0)      returns 0

Always returns a usable value. No exceptions, no unset results.

LENGTH AND EMPTY CHECK

  len <- length ages              number of entries
  isEmpty <- ages is empty        true if no entries

ITERATION

A for loop over a Dict yields DictEntry values:

  for entry in ages
    stdout.println(`${entry.key()}: ${entry.value()}`)

You can also use a helper function to format each entry.

See Q90 (What operations does Dict support?) for contains, keys/values iterators, merging, copy, comparison, JSON, and stream pipelines. See Q45 (How does the List type work?) for the sister collection type. See Q29 (How do unset variables work?) for tri-state semantics and why empty dicts are set. See Q129 for safe access patterns with missing keys. See Q126 for choosing the right collection type. See Q167 for dict iteration.

See Q260 for Dict key type requirements and using custom types as keys.

Use 'ek9 -h Dict' and 'ek9 -h DictEntry' to see the full API.

Example

defines module qa.dict

  defines function

    entryToString() as pure
      -> entry as DictEntry of (String, Integer)
      <- rtn as String: `${entry.key()}: ${entry.value()}`

  defines program
    DictDemo()
      stdout <- Stdout()

      // === DICT CREATION ===

      // Literal syntax — compiler infers Dict of (String, Integer)
      ages <- {"Alice": 30, "Bob": 25, "Charlie": 35}
      stdout.println(`Ages: ${ages}`)

      // Typed empty dict
      scores <- Dict() of (String, Integer)
      stdout.println(`Empty: ${scores}`)

      // Single-entry construction
      single <- Dict("Alice", 30)
      stdout.println(`Single: ${single}`)

      // === DICTENTRY BASICS ===

      entry <- DictEntry("Dave", 28)
      stdout.println(`Entry key: ${entry.key()}, value: ${entry.value()}`)

      // === ADDING ENTRIES ===

      // += mutates the dict
      ages += DictEntry("Dave", 28)
      stdout.println(`After += Dave: ${ages}`)

      // + creates a new dict (original unchanged)
      moreAges <- ages + DictEntry("Eve", 32)
      stdout.println(`Original: ${ages}`)
      stdout.println(`With Eve: ${moreAges}`)

      // === REMOVING ENTRIES ===

      // -= removes by key
      ages -= "Bob"
      stdout.println(`After -= Bob: ${ages}`)

      // === SAFE VALUE ACCESS ===

      aliceAge <- ages.getOrDefault("Alice", 0)
      stdout.println(`Alice age: ${aliceAge}`)

      missingAge <- ages.getOrDefault("Unknown", 0)
      stdout.println(`Missing age (default 0): ${missingAge}`)

      // === LENGTH AND EMPTY CHECK ===

      lenAges <- length ages
      stdout.println(`Length: ${lenAges}`)

      agesEmpty <- ages is empty
      stdout.println(`Ages empty: ${agesEmpty}`)

      scoresEmpty <- scores is empty
      stdout.println(`Scores empty: ${scoresEmpty}`)

      // === ITERATION ===

      // For loop yields DictEntry
      for ageEntry in ages
        stdout.println(`${ageEntry.key()} -> ${ageEntry.value()}`)

      // Using a helper function for formatting
      for ageEntry in ages
        stdout.println(entryToString(ageEntry))

Common mistakes

E50010 — EK9 uses 'Dict' not 'HashMap' or 'Map'. There is no HashMap type in EK9. See ek9 -h E50010 for details.

Incorrect:

scores <- HashMap() of (String, Integer)

Correct:

scores <- Dict() of (String, Integer)

E06010 — Dict is a generic type requiring two type parameters. An empty Dict() without 'of (K, V)' cannot be resolved. See ek9 -h E06010 for details.

Incorrect:

single <- Dict()

Correct:

single <- Dict("Alice", 30)

E50060 — EK9 Dict does not have a '.get()' method. Use '.getOrDefault(key, default)' which always returns a usable value. See ek9 -h E50060 for details.

Incorrect:

aliceAge <- ages.get("Alice", 0)

Correct:

aliceAge <- ages.getOrDefault("Alice", 0)

E50001 — EK9 uses 'length' as a prefix operator, not Python's 'len()' function. Write 'length ages' not 'len(ages)'. See ek9 -h E50001 for details.

Incorrect:

lenAges <- len(ages)

Correct:

lenAges <- length ages
Other ways to ask this
  • How do I create and use dictionaries in EK9?
  • How do I look up values safely in an EK9 Dict?
  • How do I iterate over dictionary entries in EK9?

Coming from another language?

Java: HashMap with .get() returning null (NPE risk), no literal syntax. Python: built-in dict with {} literal, .get(key, default). JavaScript: Object/Map, optional chaining. Rust: HashMap, .get() returns Option. Go: map with comma-ok idiom, nil map panics on write. Kotlin: mapOf()/mutableMapOf(), closest in convenience. EK9: {key: value} literal, Dict of (K, V), .getOrDefault() always returns a value, DictEntry for iteration.

Keywords: entry, getOrDefault, lookup, iterate, first, dict, key, literal, start, DictEntry, dictionary, add, intro, remove, generic, map, beginner