Phel ships with four go-to collections: vectors, maps, sets, and lists. They share two superpowers - immutability (every "change" returns a new value) and structural sharing (so it stays fast). Let's meet them.
Exercise 1 easy
Build a vector with the elements 2, "nice", and true.
[2 "nice" true]
; or
(vector 2 "nice" true)
Vectors are ordered collections that hold any mix of types. Square brackets are the idiomatic literal.
Learn more: Data Structures
Exercise 2 easy
Build a vector containing the keywords :hello and :world.
[:hello :world]
Keywords are lightweight identifiers prefixed with :. They're cheap, comparable by identity, and perfect for map keys or enum-like tags.
Learn more: Basic Types
Exercise 3 easy
Build a map with keys :name and :age, values "Ada" and 36.
{:name "Ada" :age 36}
; or
(hash-map :name "Ada" :age 36)
Maps are key-value collections. Keyword keys are the idiomatic default in Phel.
Learn more: Data Structures
Exercise 4 easy
Build a set from 1, 2, 3, 2. How many elements does it hold?
(hash-set 1 2 3 2)
; => #{1 2 3} - duplicates dropped
(count (hash-set 1 2 3 2))
; => 3
Sets store unique values. Adding a duplicate is a no-op.
Learn more: Data Structures
Exercise 5 easy
Use count to find the size of [10 20 30] and {:a 1 :b 2}. Then check (empty? []) and (empty? [1]).
(count [10 20 30]) ; => 3
(count {:a 1 :b 2}) ; => 2
(empty? []) ; => true
(empty? [1]) ; => false
count works on every Phel collection (and strings). empty? is the predicate version of "is the count zero?".
Learn more: Data Structures
Exercise 6 easy
Use get to read the second element from [10 20 30].
(get [10 20 30] 1)
; => 20
Vector indices are zero-based, so index 1 is the second element.
Learn more: Data Structures
Exercise 7 easy
Given this map, retrieve :name three different ways:
(def person {:name "Ada" :age 36})
(def person {:name "Ada" :age 36})
(get person :name) ; => "Ada" - explicit
(person :name) ; => "Ada" - map as function
(:name person) ; => "Ada" - keyword as function
All three are equivalent. The keyword-as-function form ((:name person)) is the most idiomatic style.
Learn more: Data Structures
Exercise 8 easy
Use get-in to grab :treasure from this nested structure:
(def dungeon {:description "dark cave"
:rooms [{:contents :monster}
nil
{:contents [:trinket :treasure]}]})
(def dungeon {:description "dark cave"
:rooms [{:contents :monster}
nil
{:contents [:trinket :treasure]}]})
(get-in dungeon [:rooms 2 :contents 1])
; => :treasure
get-in walks nested maps and vectors via a path of keys/indices.
Learn more: Data Structures
Exercise 9 easy
Use assoc to add :email "ada@example.com" to person. What does assoc return? What is person after?
(def person {:name "Ada" :age 36})
(def person {:name "Ada" :age 36})
(assoc person :email "ada@example.com")
; => {:name "Ada" :age 36 :email "ada@example.com"}
person
; => {:name "Ada" :age 36} - unchanged!
Phel's data structures are immutable. assoc returns a fresh map; the original stays as it was. This is the foundation of safe, predictable code.
Learn more: Data Structures
Exercise 10 easy
Use conj to append 4 to [1 2 3]. Verify the original length didn't change.
(def nums [1 2 3])
(def more-nums (conj nums 4))
(count nums) ; => 3 (original untouched)
(count more-nums) ; => 4
more-nums ; => [1 2 3 4]
Same immutability story: conj returns a new vector, the original stays put.
Learn more: Data Structures
Exercise 11 easy
Use contains? to check whether the set (hash-set :apple :banana :cherry) has :banana. Then check :grape.
(def fruits (hash-set :apple :banana :cherry))
(contains? fruits :banana) ; => true
(contains? fruits :grape) ; => false
contains? is the natural way to ask "is this here?" - works on sets and on map keys.
Learn more: Data Structures
Exercise 12 easy
Inspect {:a 1 :b 2 :c 3} with keys and values.
(keys {:a 1 :b 2 :c 3}) ; => [:a :b :c]
(values {:a 1 :b 2 :c 3}) ; => [1 2 3]
Useful when you only care about one side of a map.
Learn more: Data Structures
Exercise 13 easy
Use merge to combine {:name "Ada"} and {:age 36 :role :admin} into one map.
(merge {:name "Ada"} {:age 36 :role :admin})
; => {:name "Ada" :age 36 :role :admin}
merge builds a new map containing all entries. Later values win on conflicts - great for layering defaults with overrides.
Learn more: Data Structures