ANY CONTAINER IN ANY CONTAINER
Real data is nested. A school has classes, a class has students, and each student has a score for each subject. In satellite any container can hold any other container, to any depth, and the declaration says exactly what goes where. This post builds one, reaches into it, and then asks satl how to reach into it.
One declaration, three levels
Read the type from the outside in: a map from class names to lists, and each list holds maps from subjects to scores. The whole thing can be written in one literal. Braces with key: value entries are a map, and braces without them are a list.
(satellite)
satellite.capsule satellite.main()
{
satellite.container.map<satellite.variable.string, satellite.container.list<satellite.container.map<satellite.variable.string, satellite.variable.number>>> school = {"7A": {{"maths": 81, "art": 64}, {"maths": 72, "art": 90}}}
school["7A"].append({"maths": 55, "art": 70})
school["7B"] = {{"maths": 99, "art": 58}}
satellite.console.display(school["7A"][2]["art"])
school["7A"][1]["maths"] = 84
satellite.console.display(school["7A"][1])
satellite.console.display(school["7A"].size)
satellite.console.display(school.keys)
satellite.return(satellite)
}90
{"maths": 84, "art": 64}
3
{"7A", "7B"}Each [ ] steps one level down, left to right. school["7A"] is a list of students. school["7A"][2] is the second student, a map, because a list counts from 1. school["7A"][2]["art"] is that student’s art score. Writing works the same way: school["7A"][1]["maths"] = 84 changes one number deep inside, and .append on a list inside adds a student to that class.
Checked all the way down
What you declare is a promise, and satl holds you to it on every way in: in the literal, in .append, and in a write three levels down. A score is a number, so a string cannot go there.
(satellite)
satellite.capsule satellite.main()
{
satellite.container.map<satellite.variable.string, satellite.container.list<satellite.container.map<satellite.variable.string, satellite.variable.number>>> school = {"7A": {{"maths": 81}}}
school["7A"][1]["maths"] = "top of the class"
satellite.return(satellite)
}--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S301: TYPES_DO_NOT_MEET
satl(run): in school[...] = ..., the value does not fit: it holds a string
directory: school_wrong.satl:6
syntax: school["7A"][1]["maths"] = "top of the class"
/\
this operator has no scenario for the two kinds it was given. Nothing was
guessed at, because a guess here is an answer that is wrong and does not say so.
machine code 27 types_do_not_meet -- satl exits with this.
--------------------------------------------------------------------------------Asking satl how to reach in: satellite.access
With a shape this deep it is easy to lose track of which [ ] is which. satellite.access(name) answers that from the declaration. It says what the name is and what it holds, gives one line for each level you can reach, and gives one line that fills each level with a value of the right shape.
(satellite)
satellite.capsule satellite.main()
{
satellite.container.map<satellite.variable.string, satellite.container.list<satellite.container.map<satellite.variable.string, satellite.variable.number>>> school = {"7A": {{"maths": 81, "art": 64}}}
satellite.access(school)
satellite.return(satellite)
}school is a map (string -> list of maps (string -> number)), 1 key
value {"7A": {{"maths": 81, "art": 64}}}
school["key"] a list of maps (string -> number), "key" a string, one of "7A"
school["key"][n] a map (string -> number), n counting from 1
school["key"][n]["key2"] a number, "key2" a string
school["key"] = {{"key": 1}} puts a list of maps under "key"
school["key"].append({"key": 1}) adds a map
school["key"][n]["key2"] = 1 puts a number under "key2"
school.size how many keys it holds
school.keys the keys of it, as a list
school["key"][n].keys the keys of a map, as a listn stands for a position, counting from 1, and "key" and "key2" stand for keys. The second column says what each line reaches. None of it is guessed: satl’s tests generate thousands of container shapes, ask satellite.access about each one, and run every line it prints as code.
When an item does not fit
Here is a real first attempt at a deep container. It declares a list of lists of maps, and then appends {{"hello"}}:
(satellite)
satellite.capsule satellite.main()
{
satellite.container.list<satellite.container.list<satellite.container.map<satellite.variable.string, satellite.container.list<satellite.container.list<satellite.variable.string>>>>> crazy_container
crazy_container.append({{"hello"}})
satellite.return(satellite)
}--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S301: TYPES_DO_NOT_MEET
satl(run): in crazy_container, crazy_container.append: item 1 of it does not
fit: it holds a list
directory: crazy_container.satl:6
syntax: crazy_container.append({{"hello"}})
/\
this operator has no scenario for the two kinds it was given. Nothing was
guessed at, because a guess here is an answer that is wrong and does not say so.
machine code 27 types_do_not_meet -- satl exits with this.
--------------------------------------------------------------------------------The declaration is right. The item is what went wrong: each item of crazy_container is a list of maps, and {"hello"} is a list, not a map. satl never guesses what a line was meant to say, but it knows the declaration, so satellite.access(crazy_container) shows the line that fits. The item needs a key before its lists:
(satellite)
satellite.capsule satellite.main()
{
satellite.container.list<satellite.container.list<satellite.container.map<satellite.variable.string, satellite.container.list<satellite.container.list<satellite.variable.string>>>>> crazy_container
crazy_container.append({{"greeting": {{"hello"}}}})
satellite.console.display(crazy_container[1][1]["greeting"][1][1])
satellite.access(crazy_container)
satellite.return(satellite)
}hello
crazy_container is a list of lists of maps (string -> list of lists of strings), 1 item
value {{{"greeting": {{"hello"}}}}}
crazy_container[n] a list of maps (string -> list of lists of strings), n counting from 1 (1 to 1)
crazy_container[n][m] a map (string -> list of lists of strings), m counting from 1
crazy_container[n][m]["key"] a list of lists of strings, "key" a string
crazy_container[n][m]["key"][p] a list of strings, p counting from 1
crazy_container[n][m]["key"][p][q] a string, q counting from 1
crazy_container.append({{"key": {{"text"}}}}) adds a list of maps
crazy_container[n].append({"key": {{"text"}}}) adds a map
crazy_container[n][m]["key"] = {{"text"}} puts a list of lists of strings under "key"
crazy_container[n][m]["key"].append({"text"}) adds a list of strings
crazy_container[n][m]["key"][p].append("text") adds a string
crazy_container.size how many lists of maps it holds
crazy_container[n][m].keys the keys of a map, as a listWhere next
Each container on its own: LISTS, GRIDS: LISTS OF LISTS, MAPS and MULTIPLE: ONE NAME, SEVERAL TYPES. satellite.access and the switch behind it are in HISTORY AND ACCESS, and every container word is in the FULL REFERENCE.
