Sets are like magical bags of objects that are also found somewhere else in the code: variables, functions, classes, other sets, whatever. To define a set we write curly braces around a list of objects separated by commas (or end-of-lines). Here is an example where each object is in several places.
Alice :: Bob :: Christine :: Daniel :: Joe :: friend_of_mine
men :: { Daniel, Joe, Bob }
neighbors :: { Christine, Bob }
cleaning_schedule :: { Bob, Alice, Bob, Alice, Joe, Joe, Daniel }
We can access members of a set using square brackets (as if they were arrays). So after running the code, Bob, men[3], neighbors[2], cleaning_schedule[1] and cleaning_schedule[3] are all the same thing. If we set men[3].needsSleep = true then Bob.needsSleep and neighbors[2].needsSleep will also be true. Notice how the same object can even appear in several places in a set. Sets come handy when the objects have convoluted path names.
to_buy :: {
food.produce.fruits.apples
clothes.shoes.black
clothes.socks.black
}
to_buy[1] is much quicker than food.produce.fruits.apples.
Cicada sets are pretty flexible in what they can store. Along with variables, we can throw constants, other sets (including inlined subsets), and even the void into the bag.
collections :: { men, neighbors, { Patty, Don }, "Herbert", 3.3, this[3], this, nothing }
Here collections[3] is an inlined subset containing two objects. The fourth and fifth items are inlined constants. collections[6] is basically an alias for the third item, the subset containing Patty and Don, and notice that it had to be listed after the third item because otherwise this[3] would not have existed yet. collections[7] is collections itself -- so collections[7][7][7][2] is just neighbors. Trying to print collections from the command line won’t work because of the infinite self reference.
The reason we access set elements by their index (as opposed to name) is that those set elements have no name, at least the way we defined them in our examples. In other words, collections[1] is effectively an alias for men, but that doesn’t mean it has the name men---typing collection.men will cause Cicada to draw a blank. But there is actually a way to name certain elements of a set, and that is to manually define aliases for their respective objects. To illustrate, here we give a different way of defining collections which assigns names to members 2, 3, 4, 7 and 8.
collections :: {
men
neighbors := @parent.neighbors | use same name for convenience
peeps :: { Patty, Don }
Herby := "Herbert"
3.3, this[3] | set elements 5 and 6
myself := @this
zilch := @nothing
}
With the verbose definition we can write collections.Herby in place of collections[4], although collections[4] is still perfectly legal.
Just as with composite variables, items can be added to and removed from sets after they have been defined. So if we want to shuffle “Herbert’’ to the end of the set we might write:
collections[top+1] := @collections.Herby
remove collections.Herby
As with variables, we can use one set to define another. The following is legal:
newCollections :: collections
Importantly, newCollections is defined using the old set collections’s original definition. So even if we had rearranged the elements of collections, in the new set Herbert will be both at newCollections[4] and newCollections.Herby. We might as well have defined both sets on the same line:
newCollections :: collections :: { ... }
Here is a tricky situation:
f :: ...
g :: { ; r :: {}, r[+1]:=args[1], return r }
rtrns :: { f(5), f(12), g(3) }
Each set element is a copy of a function’s return value, as if it was defined using a := operator. Normally this is fine, but here g()’s return value ‘r’ cannot be copied using that operator, because its structure {int} doesn’t match its original definition {}! In this case we can copy the return value ‘by reference’ instead, by making an anonymous alias:
rtrns :: { f(5), f(12), @g(3) }
Let’s just hope f() doesn’t have the same problem, because if we alias both f() calls then the second call will overwrite the first. This potential pitfall also lurks in function arguments, since an argument list is simply a set passed into a function.
Last update: November 12, 2025