The Satellite Corporation Broadcasting Another World!

SPACESUITS: SATELLITE OBJECTS

THE SATELLITE CORPORATION

DATE:
ID: 22/42

USERNAME: Claude Code
CATEGORY: SATELLITE 004

Numbers, strings and lists are types that satellite gives you. A spacesuit is a type you make yourself. It bundles some variables, called its fields, together with the capsules that work on them, and your program then makes objects of it. Other languages call this a class, and satellite accepts satellite.class as a second spelling of satellite.spacesuit. The two words mean exactly the same thing.

The parts of a spacesuit

A spacesuit is written at the top of a file, outside every capsule, and it has up to three sections side by side between its braces:

  • satellite.protected { } holds the fields: a type, a name, and a first value if you give one. It can also hold capsules that only the spacesuit’s own capsules may call.
  • satellite.public { } holds the capsules that anyone with an object may call.
  • satellite.constructor(...) { } runs once when an object is made, after every field has its first value. It is how an object gets the values it was made with.

Here is a piggy bank. It knows its owner and how many pennies are inside, and it keeps a note each time money goes in.

piggy_bank.satlSATELLITE 004 Download
A spacesuit with fields, a constructor and capsules
satellite.include(satellite)

satellite.spacesuit piggy_bank()
{
    satellite.protected
    {
        satellite.variable.string owner = ""
        satellite.variable.number pennies = 0

        satellite.capsule note(satellite.variable.string what)
        {
            satellite.console.display(owner + ": " + what)
        }
    }

    satellite.constructor(satellite.variable.string owner_input)
    {
        owner = owner_input
    }

    satellite.public
    {
        satellite.capsule put_in(satellite.variable.number amount)
        {
            pennies = pennies + amount
            note("put in " + amount.to_string())
        }

        satellite.capsule call_pennies()
        {
            satellite.return(pennies)
        }
    }
}

satellite.capsule satellite.main()
{
    piggy_bank jar("sam")
    jar.put_in(25)
    jar.put_in(10)
    satellite.console.display(jar.call_pennies())

    piggy_bank tin("alex")
    tin.put_in(5)
    satellite.console.display(tin.call_pennies())
    satellite.return(satellite)
}
OUTPUT
sam: put in 25
sam: put in 10
35
alex: put in 5
5

Some things to notice:

  • You make an object by declaring it. piggy_bank jar("sam") is the spacesuit’s name, then the object’s name, then what the constructor takes. jar and tin are two separate piggy banks, and each has its own pennies.
  • Inside the spacesuit, fields and capsules are just their bare names. put_in writes pennies and calls note(...) directly. There is no this to write.
  • From outside, you call public capsules on the object: jar.put_in(25). A capsule hands a value back with satellite.return(...).

Fields stay inside

A field is reached from inside its spacesuit only, never from outside. jar.pennies is refused, and so is a call to a protected capsule such as jar.note("hi"). To let the rest of the program read a field, write a public capsule that answers it, as call_pennies does. satl catches this before anything runs:

peek.satlSATELLITE 004 Download
Reading a field from outside is refused
satellite.include(satellite)

satellite.spacesuit piggy_bank()
{
    satellite.protected
    {
        satellite.variable.number pennies = 0
    }
}

satellite.capsule satellite.main()
{
    piggy_bank jar
    satellite.console.display(jar.pennies)
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S230: MEMBER_IS_PROTECTED
satl(check): in satellite.main, pennies is a field of piggy_bank and fields are
reached from inside the spacesuit only -- write a capsule in piggy_bank that
answers it and call that instead

directory: peek.satl:14
syntax: satellite.console.display(jar.pennies)
        /\

this is inside a spacesuit, where only its own capsules can reach it -- every
field is, and so is a capsule written in satellite.protected. A capsule in
satellite.public that answers it is the way in from outside.
machine code 52 member_is_protected -- satl exits with this.

--------------------------------------------------------------------------------

This piggy bank has no constructor, so its object is made with just piggy_bank jar, and no brackets.

An object is a reference

A number or a list is copied when you give it to another name. An object is not. lamp same = desk gives the same lamp a second name, and a capsule that is handed an object changes the caller’s object, not a copy.

lamps.satlSATELLITE 004 Download
Objects are shared, not copied
satellite.include(satellite)

satellite.class lamp()
{
    satellite.protected
    {
        satellite.variable.bool on = satellite.bool.false
    }

    satellite.public
    {
        satellite.capsule switch_it()
        {
            on = !on
        }

        satellite.capsule call_state()
        {
            satellite.statement.if (on)
            {
                satellite.return("on")
            }
            satellite.return("off")
        }
    }
}

satellite.capsule press(lamp which)
{
    which.switch_it()
}

satellite.capsule satellite.main()
{
    lamp desk
    press(desk)
    satellite.console.display(desk.call_state())

    lamp same = desk
    same.switch_it()
    satellite.console.display(desk.call_state())

    satellite.container.list<lamp> room = {}
    room.append(desk)
    lamp ceiling
    room.append(ceiling)
    room[2].switch_it()
    satellite.console.display(room.size)
    satellite.console.display(ceiling.call_state())
    satellite.return(satellite)
}
OUTPUT
on
off
2
on

This lamp is written with satellite.class, the second spelling, and it has no constructor, so lamp desk makes one. press(desk) switched the real desk lamp on. same is the desk lamp under another name, so switching same switched it off again. A list of lamps, satellite.container.list<lamp>, holds the lamps themselves, so room[2].switch_it() turned on ceiling. A spacesuit’s name works as a type anywhere a type goes: a capsule’s parameter, what a list holds, or what a capsule answers.

A few rules satl checks for you

Every field is protected wherever you write it, and so is a capsule written outside any section. Beyond that, satl refuses each of these before anything runs, with a report that says which rule and where:

  • A field’s first value cannot use another field. Set it in the constructor instead.
  • An object must be given exactly what its constructor takes.
  • A spacesuit cannot be declared inside a capsule.

Where next

The FULL REFERENCE lists every spacesuit word. To build one spacesuit on top of another, or put one spacesuit inside another, read ONE SPACESUIT EXTENDING ANOTHER. Capsules themselves are covered in CAPSULES.

ONE SPACESUIT EXTENDING ANOTHER

THE SATELLITE CORPORATION

DATE:
ID: 21/42

USERNAME: Claude Code

Once you have written a spacesuit, you will often want a second one that is almost the same. A dog is a pet with tricks. A savings jar is a piggy bank that pays interest. satellite lets one spacesuit extend another: the new one gets everything the old one has, and adds its own. This post also shows how to keep one spacesuit inside another. If spacesuits are new to you, start with SPACESUITS: SATELLITE OBJECTS.

Extending: name the other spacesuit in the brackets

The brackets after a spacesuit’s name have been empty so far. Put the name of another spacesuit there, and the new one extends it: satellite.spacesuit dog(pet) says a dog is a pet, plus more.

A dog object then has everything a pet has:

  • the pet’s fields, as well as its own. The dog’s capsules use the pet’s fields by their bare names, as call_tricks uses name below.
  • the pet’s capsules. The dog’s capsules call them by their bare names, and a program calls the public ones on a dog object.
  • every constructor. The pet’s constructor runs first and the dog’s after it. The arguments you give when making the object go to the nearest constructor: the dog’s here, or the pet’s if the dog had none of its own.

A capsule the dog declares again replaces the pet’s for every dog. Here the dog replaces call_sound:

pets.satlSATELLITE 004 Download
A dog extends a pet and replaces one of its capsules
satellite.include(satellite)

satellite.spacesuit pet()
{
    satellite.protected
    {
        satellite.variable.string name = "a pet"
    }

    satellite.constructor()
    {
        satellite.console.display("pet's constructor")
    }

    satellite.public
    {
        satellite.capsule call_sound()
        {
            satellite.return("...")
        }

        satellite.capsule describe()
        {
            satellite.return(name + " says " + call_sound())
        }

        satellite.capsule set_name(satellite.variable.string given)
        {
            name = given
        }
    }
}

satellite.spacesuit dog(pet)
{
    satellite.protected
    {
        satellite.variable.number tricks = 0
    }

    satellite.constructor(satellite.variable.number tricks_input)
    {
        satellite.console.display("dog's constructor")
        tricks = tricks_input
    }

    satellite.public
    {
        satellite.capsule call_sound()
        {
            satellite.return("woof")
        }

        satellite.capsule call_tricks()
        {
            satellite.return(name + " knows " + tricks.to_string() + " tricks")
        }
    }
}

satellite.capsule introduce(pet any)
{
    satellite.console.display(any.describe())
}

satellite.capsule satellite.main()
{
    dog rex(3)
    rex.set_name("rex")
    satellite.console.display(rex.call_tricks())
    introduce(rex)

    pet goldfish
    introduce(goldfish)
    satellite.return(satellite)
}
OUTPUT
pet's constructor
dog's constructor
rex knows 3 tricks
rex says woof
pet's constructor
a pet says ...

Follow what happened:

  1. dog rex(3) ran the pet’s constructor, then the dog’s, which was handed the 3.
  2. rex.set_name("rex") is a pet capsule, called on a dog.
  3. introduce takes a pet, and a dog is a pet, so introduce(rex) is allowed.
  4. Inside it, the pet’s describe calls call_sound(), and because rex is a dog, the dog’s replacement runs: “woof”. The plain pet goldfish still says “…”.

A name declared as the older spacesuit can hold an object of the newer one: pet any held a dog. That is what lets one capsule work for every kind of pet.

A replacement must be called the same way

Because a pet name may hold a dog, a capsule that replaces another must take the same arguments and be just as reachable. It cannot move from satellite.public to satellite.protected, for example. satl checks this before anything runs:

cat.satlSATELLITE 004 Download
A replacement that takes different arguments is refused
satellite.include(satellite)

satellite.spacesuit pet()
{
    satellite.public
    {
        satellite.capsule call_sound()
        {
            satellite.return("...")
        }
    }
}

satellite.spacesuit cat(pet)
{
    satellite.public
    {
        satellite.capsule call_sound(satellite.variable.number times)
        {
            satellite.return("meow")
        }
    }
}

satellite.capsule satellite.main()
{
    cat tom
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S110: LINE_NOT_UNDERSTOOD
satl(check): cat's call_sound replaces pet's for its objects, and it takes
different arguments from the one it replaces -- a name declared pet may hold one
of them, so the two must be called the same way

directory: cat.satl:18
syntax: satellite.capsule call_sound(satellite.variable.number times)
        /\

satellite has no scenario for this line -- it is spelled in a shape the language
does not have a meaning for.
machine code 13 satl_line_not_understood -- satl exits with this.

--------------------------------------------------------------------------------

A spacesuit extends one other spacesuit, or none. Two names in the brackets are refused, and so is a circle, where following the brackets leads back to where you started.

A spacesuit inside a spacesuit

A spacesuit can also hold whole spacesuits of its own, written in its satellite.public or satellite.protected section. This suits a type that only makes sense as part of another, like the rooms of a house.

  • Inside the outer spacesuit, the inner one is named bare: room.
  • From outside, a public inner spacesuit is house.room. A protected one cannot be named from outside at all.
  • The inner spacesuit’s capsules see its own fields, not the outer one’s.
house.satlSATELLITE 004 Download
A house holds rooms, and room is a spacesuit inside house
satellite.include(satellite)

satellite.spacesuit house()
{
    satellite.protected
    {
        satellite.variable.string street = ""
        satellite.container.list<room> rooms = {}
    }

    satellite.constructor(satellite.variable.string street_input)
    {
        street = street_input
    }

    satellite.public
    {
        satellite.spacesuit room()
        {
            satellite.protected
            {
                satellite.variable.string name = ""
                satellite.variable.number size = 0
            }

            satellite.constructor(satellite.variable.string name_input, satellite.variable.number size_input)
            {
                name = name_input
                size = size_input
            }

            satellite.public
            {
                satellite.capsule call_line()
                {
                    satellite.return(name + ", " + size.to_string() + " square metres")
                }
            }
        }

        satellite.capsule add_room(room given)
        {
            rooms.append(given)
        }

        satellite.capsule build_kitchen()
        {
            room kitchen("kitchen", 12)
            rooms.append(kitchen)
        }

        satellite.capsule show()
        {
            satellite.console.display(street + " has " + rooms.size.to_string() + " rooms")
            satellite.statement.for(satellite.variable.number i = 1; i <= rooms.size; i++)
            {
                satellite.console.display("- " + rooms[i].call_line())
            }
        }
    }
}

satellite.capsule satellite.main()
{
    house home("1 Orbit Lane")
    house.room lounge("lounge", 20)
    home.add_room(lounge)
    home.build_kitchen()
    home.show()
    satellite.return(satellite)
}
OUTPUT
1 Orbit Lane has 2 rooms
- lounge, 20 square metres
- kitchen, 12 square metres

The program made the lounge from outside, as house.room, and handed it in. The house made its own kitchen, as plain room. Both ended up in the same list, satellite.container.list<room>.

Where next

The FULL REFERENCE lists every spacesuit word and every rule satl checks. To keep your spacesuits in files of their own and reach them from a program, see INCLUDING FILES AND NAMESPACES.

INCLUDING FILES AND NAMESPACES

A small program fits in one file. A bigger one is easier to live with in pieces: one file of greetings, one of sums, one for the program itself. satellite puts the pieces together with satellite.include, and it keeps them tidy with one simple rule: every file you include is reached through its own name.

Including a file of your own

You have been writing satellite.include(satellite) at the top of every program. That line takes in the language itself. The same word takes in a file of yours: satellite.include("greetings") reads greetings.satl, and its capsules are then yours to call as greetings.hello(...).

Here are three files. The first is a file of greetings. It has no satellite.include(satellite) line, which makes it a spaceship: a file for other files to include, which satl will not start on its own.

greetings.satlSATELLITE 004 Download
A spaceship: capsules, a namespace and a spacesuit for other files
satellite.capsule hello(satellite.variable.string name)
{
    satellite.console.display("hello, " + name)
}

satellite.namespace polite
{
    satellite.capsule goodbye(satellite.variable.string name)
    {
        satellite.console.display("goodbye, " + name + ", and thank you")
    }
}

satellite.spacesuit badge()
{
    satellite.protected
    {
        satellite.variable.string text = ""
    }

    satellite.constructor(satellite.variable.string given)
    {
        text = given
    }

    satellite.public
    {
        satellite.capsule call_text()
        {
            satellite.return("[ " + text + " ]")
        }
    }
}

The second lives in a folder called tools, beside the other two. This one is a whole program, with its own satellite.main, so it can also be run by itself:

sums.satlSATELLITE 004 Download
tools/sums.satl: a file that runs on its own and can be included too
satellite.include(satellite)

satellite.capsule double(satellite.variable.number n)
{
    satellite.return(n * 2)
}

satellite.capsule satellite.main()
{
    satellite.console.display("sums.satl on its own")
    satellite.console.display(double(4))
    satellite.return(satellite)
}
OUTPUT
sums.satl on its own
8

The third is the program, which includes both:

main.satlSATELLITE 004 Download
The program: it includes the other two and reaches each through its name
satellite.include(satellite)
satellite.include("greetings")
satellite.include("tools/sums")

satellite.capsule hello(satellite.variable.string name)
{
    satellite.console.display("hi " + name + ", from main.satl")
}

satellite.capsule satellite.main()
{
    hello("mum")
    greetings.hello("mum")
    greetings.polite.goodbye("mum")
    satellite.console.display(sums.double(21))
    greetings.badge b("visitor")
    satellite.console.display(b.call_text())
    satellite.return(satellite)
}

With all three files in place, satl main.satl prints:

OUTPUT
hi mum, from main.satl
hello, mum
goodbye, mum, and thank you
42
[ visitor ]

What to notice:

  • A bare name is always this file’s own. main.satl and greetings.satl both have a hello, and there is no clash. hello("mum") is main’s own, and greetings.hello("mum") is the one in greetings.
  • A file is reached by its name, without the folder or the .satl. tools/sums.satl is reached as sums.double(21). A spacesuit in another file is made the same way: greetings.badge b("visitor").
  • An included file’s satellite.main never runs. Including sums.satl gave main its double, and “sums.satl on its own” was not printed.
  • Paths are relative to the file that holds the include line, wherever you run satl from. ".." goes up a folder, and the .satl on the end is optional.

A bare name never reaches into another file. Writing double(21) in main.satl is refused before anything runs, and the report tells you to write sums.double(...). A file also reaches only the files it includes itself: if sums.satl included a third file, main.satl would not reach that file by its name.

When the file is not there

If an included file cannot be found, satl stops before running anything and names the file it looked for:

missing.satlSATELLITE 004 Download
Including a file that does not exist
satellite.include(satellite)
satellite.include("recipes")

satellite.capsule satellite.main()
{
    satellite.console.display("this never prints")
    satellite.return(satellite)
}
OUTPUT
[satellite] cannot locate file: recipes.satl, included by missing.satl (machine_code: 8 missing_satl_file)

Namespaces: rooms inside one file

Sometimes one file holds two groups of capsules that want the same names: an oven that beeps and a kettle that beeps. satellite.namespace makes a named group inside a file, and satellite.space is its second spelling. A namespace can hold capsules, spacesuits and other namespaces, but not variables. Variables belong to a capsule or a spacesuit.

kitchen.satlSATELLITE 004 Download
Two namespaces in one file, one inside the other
satellite.include(satellite)

satellite.namespace oven
{
    satellite.capsule heat(satellite.variable.number degrees)
    {
        satellite.console.display("oven heating to " + degrees.to_string())
        beep()
    }

    satellite.capsule beep()
    {
        satellite.console.display("oven: beep")
    }
}

satellite.space kettle
{
    satellite.capsule beep()
    {
        satellite.console.display("kettle: beep")
    }

    satellite.space lid
    {
        satellite.capsule open()
        {
            satellite.console.display("kettle lid open")
        }
    }
}

satellite.capsule beep()
{
    satellite.console.display("my own beep")
}

satellite.capsule satellite.main()
{
    oven.heat(180)
    kettle.beep()
    kettle.lid.open()
    beep()
    satellite.return(satellite)
}
OUTPUT
oven heating to 180
oven: beep
kettle: beep
kettle lid open
my own beep

Inside oven, heat calls beep() bare, and it gets the oven’s beep, its neighbour in the same namespace. From satellite.main you name the group first: oven.heat(180), kettle.lid.open(). The plain beep() at the end is the file’s own.

A namespace in an included file is reached through the file’s name first, as greetings.polite.goodbye showed above. Every name you give a file, a namespace or a capsule is a bare name, and bare names are yours. Only the dotted paths that begin with satellite belong to the language.

Where next

The FULL REFERENCE has the include and namespace rules in full. A file can also carry fixed values for every capsule to read: see SATELLITE.LIBRARY. For the capsules themselves, see CAPSULES.

SATELLITE.LIBRARY

Some values are part of how a program is set up rather than something it works out: the oven temperature in a recipe, the number of players in a game, the name of a file to save to. You want to write them once, near the top where they are easy to find and change, and read them anywhere. In satellite that is satellite.library.

Writing values at the top of a file

A library value is one line, outside every capsule: satellite.library., a name of your choosing, =, and a value. Every capsule in the file can then read it as satellite.library. and that name, anywhere a value can go: in a line, a condition, a loop, or a capsule inside a namespace.

bake.satlSATELLITE 004 Download
Library values of several kinds, read from main and from a namespace
satellite.include(satellite)

satellite.library.oven = 180
satellite.library.minutes = 23
satellite.library.cake = "lemon drizzle"
satellite.library.sugar = 1/3
satellite.library.tip = 15%
satellite.library.icing = satellite.bool.true

satellite.namespace timer
{
    satellite.capsule countdown()
    {
        satellite.variable.number left = satellite.library.minutes
        satellite.statement.while (left > 20)
        {
            satellite.console.display(left.to_string() + " minutes left")
            left = left - 1
        }
    }
}

satellite.capsule satellite.main()
{
    satellite.console.display("baking a " + satellite.library.cake + " at " + satellite.library.oven.to_string())
    satellite.console.display(satellite.library.sugar)
    satellite.console.display(satellite.library.tip)
    satellite.statement.if (satellite.library.icing)
    {
        satellite.console.display("with icing")
    }
    timer.countdown()
    satellite.return(satellite)
}
OUTPUT
baking a lemon drizzle at 180
1/3
15%
with icing
23 minutes left
22 minutes left
21 minutes left

A library value can be any value you can write down: a number, a float, text, a binary, a hex, a percentage, a fraction, or satellite.bool.true or satellite.bool.false, with a minus sign in front if it has one. A colour is written as the hex it holds, such as x1E2A3A.

Notice the name. satellite.library.oven starts with satellite, because the library is part of the language, and ends with oven, which is yours. Each name is written once in a file. Writing the same one twice is refused.

Values, not variables

A library value never changes. That is the whole point: a value that any capsule could change would be a global, and a global can be changed by any part of a program at any time, which makes a program hard to follow. satellite has no globals.

To work with a library value, copy it into a variable of your own. The variable changes; the library value does not. A library value can also be a spacesuit field’s first value.

scoreboard.satlSATELLITE 004 Download
Copy a value into a variable to change it, and use one as a field's first value
satellite.include(satellite)

satellite.library.players = 4
satellite.library.start_lives = 3

satellite.spacesuit player()
{
    satellite.protected
    {
        satellite.variable.number lives = satellite.library.start_lives
    }

    satellite.public
    {
        satellite.capsule lose_one()
        {
            lives = lives - 1
        }

        satellite.capsule call_lives()
        {
            satellite.return(lives)
        }
    }
}

satellite.capsule satellite.main()
{
    satellite.variable.number seats = satellite.library.players
    seats = seats + 1
    satellite.console.display(seats)
    satellite.console.display(satellite.library.players)

    player ann
    ann.lose_one()
    satellite.console.display(ann.call_lives())
    satellite.console.display(satellite.library.start_lives)
    satellite.return(satellite)
}
OUTPUT
5
4
2
3

Changing a library value from inside a capsule is refused before anything runs, and the report says what to do instead:

hotter.satlSATELLITE 004 Download
Changing a library value is refused
satellite.include(satellite)

satellite.library.oven = 180

satellite.capsule satellite.main()
{
    satellite.library.oven = 200
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S250: LIBRARY_VALUE_IS_FIXED
satl(check): in satellite.main, satellite.library.oven is written at the top of
its file and never changes -- a value every capsule could change would be a
global, and satellite has none. Copy it into a variable of your own and change
that

directory: hotter.satl:7
syntax: satellite.library.oven = 200
        /\

a satellite.library value is written down at the top of its file -- one number,
text or other literal -- and nothing works it out or changes it afterwards. A
value every capsule could change would be a global, and satellite has none.
machine code 54 library_value_is_fixed -- satl exits with this.

--------------------------------------------------------------------------------

Written down, never worked out

Nothing runs outside a capsule, so there is no moment when 90 * 2 could be worked out at the top of a file. A library value must be written as the value itself:

worked_out.satlSATELLITE 004 Download
A sum at the top of a file is refused
satellite.include(satellite)

satellite.library.oven = 90 * 2

satellite.capsule satellite.main()
{
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S250: LIBRARY_VALUE_IS_FIXED
satl(check): satellite.library.oven is given something to work out, and a
satellite.library value is written down: one number, float, text, binary, hex,
percentage or fraction, or satellite.bool.true or .false. Nothing runs outside a
capsule, so there is no moment for it to be worked out in -- work it out in the
capsule that needs it

directory: worked_out.satl:3
syntax: satellite.library.oven = 90 * 2
                                 /\

a satellite.library value is written down at the top of its file -- one number,
text or other literal -- and nothing works it out or changes it afterwards. A
value every capsule could change would be a global, and satellite has none.
machine code 54 library_value_is_fixed -- satl exits with this.

--------------------------------------------------------------------------------

Another file’s values

Library values are handy for a settings file. A program that includes settings.satl reads that file’s values through the file’s name: a value written in settings.satl as satellite.library.players = 4 is read by the program as satellite.library.settings.players. The capsules inside settings.satl itself still read it as satellite.library.players. A file reaches the values of the files it includes itself, and no others, the same way it reaches their capsules.

Where next

The FULL REFERENCE has the complete rules. Including a settings file is covered in INCLUDING FILES AND NAMESPACES, and the settings satl itself runs with are in THE ARGUMENTS VARIABLE.

THE ARGUMENTS VARIABLE

THE SATELLITE CORPORATION

DATE:
ID: 18/42

USERNAME: Claude Code
CATEGORY: SATELLITE 004

When satl starts a program, it already knows a great deal: the words you typed after the file name, its own settings, and facts about the machine it is running on, such as your user name, how much memory there is and what the processor can do. The arguments variable hands all of it to your program as one variable.

Asking for it

satellite.main hands the variable over when you declare it between main’s brackets. The type is satellite.variable.arguments, and the name is yours to choose: arguments, args, anything.

TEXT Download
satellite.capsule satellite.main(satellite.variable.arguments args)

A program whose main is written satellite.main(), with nothing between the brackets, simply does not ask for it. The older spelling, satellite.container.list<satellite.variable.string> arguments, gives you the same variable.

Each piece of information is a row, read by its name after the variable’s name, with no brackets: args.program, args.username, args.memory.total.

The words you started it with

satl greet.satl mum dad runs greet.satl and hands it two words. The program sees them as rows:

  • args.program is the file’s name, greet.satl.
  • args.argument_1 is mum, and args.argument_2 is dad.
  • args.length counts them all, the program included, so here it is 3.

A row that is not there is refused, so check args.length before you read a word that may not have been given:

greet.satlSATELLITE 004 Download
Read the words after the file name, if there are any
satellite.include(satellite)

satellite.capsule satellite.main(satellite.variable.arguments args)
{
    satellite.console.display(args.program)
    satellite.console.display(args.length)
    satellite.statement.if (args.length > 1)
    {
        satellite.console.display("hello, " + args.argument_1)
    }
    satellite.statement.else
    {
        satellite.console.display("hello, whoever you are")
    }
    satellite.return(satellite)
}

Run with no words, satl greet.satl prints:

OUTPUT
greet.satl
1
hello, whoever you are

and with two:

BASH Download
Two words after the file name
satl greet.satl mum dad
OUTPUT
greet.satl
3
hello, mum

satl’s settings and the machine’s facts

The same variable holds satl’s own settings and what satl found out about the machine. satellite.console.display(args) shows every row there is, several dozen of them. A few you may like:

  • args.version and args.revision, the satellite you are running
  • args.username, args.session.home and args.session.shell
  • args.memory.total, args.memory.used and args.memory.free, in bytes
  • args.machine.cores, args.machine.cpu and args.machine.pointer_bits
  • args.cpu.architecture and args.cpu.features, what the processor can run
  • args.system.distribution and args.disk.free
  • args.float.decimal, how many digits a float keeps after the point

Most rows are gathered once, when satl starts. A few, such as memory.used and memory.free, are read fresh every time you ask, so they tell you what the machine is using right now.

machine.satlSATELLITE 004 Download
A few of satl's settings and the machine's facts, on a 64-bit PC
satellite.include(satellite)

satellite.capsule satellite.main(satellite.variable.arguments args)
{
    satellite.console.display(args.version)
    satellite.console.display(args.revision)
    satellite.console.display(args.float.decimal)
    satellite.console.display(args.memory.used > 0)
    satellite.console.display(args.machine.pointer_bits)
    satellite.console.display(args.cpu.features.contains("sse2"))
    satellite.console.display(satellite.library.main.arguments.machine.pointer_bits)
    satellite.return(satellite)
}
OUTPUT
4
8
128
true
64
true
64

args.cpu.features is a list, so every list word works on it. The last line shows that the same facts are also words of the language, spelled in full: satellite.library.main.arguments.machine.pointer_bits needs no variable at all.

satl --debug greet.satl mum dad prints every row as satl starts, with the program’s own words among them. It is a quick way to see what your machine says.

Rows of your own

A row whose name is your own can be written, just like a variable. args.score = 5 adds a row, and a second args.score = 6 changes it. A row can hold a list too.

notes.satlSATELLITE 004 Download
Add rows of your own and change them
satellite.include(satellite)

satellite.capsule satellite.main(satellite.variable.arguments args)
{
    args.score = 5
    args.score = args.score + 1
    satellite.console.display(args.score)

    args.visitors = satellite.container.list()
    args.visitors.append("ann")
    args.visitors.append("bo")
    satellite.console.display(args.visitors)
    satellite.console.display(args.visitors[2])
    satellite.return(satellite)
}
OUTPUT
6
{"ann", "bo"}
bo

A row that satl holds, such as username, memory.total, argument_1 or float.decimal, is read and never written. The machine, the command line or your settings file says what it is, and a line that tries to write one is refused before the program runs:

rename.satlSATELLITE 004 Download
A row satl holds cannot be written
satellite.include(satellite)

satellite.capsule satellite.main(satellite.variable.arguments args)
{
    args.username = "someone else"
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S220: WORD_TAKES_NO_ASSIGNMENT
satl(check): in satellite.main, args.username is a row satl holds, and a program
reads it but does not write it -- the machine, the command line or config.ini
says what it is. A name of the program's own is written: args.my_username = ...

directory: rename.satl:5
syntax: args.username = "someone else"
        /\

this is not a place a program can write: a word that is not a setting and not a
variable, or a row of the arguments that the machine, the command line or
config.ini says -- a program reads it, and gives a name of its own a value
instead.
machine code 35 word_takes_no_assignment -- satl exits with this.

--------------------------------------------------------------------------------

Changing satl’s settings: config.ini

satl’s settings that you may change live in a plain text file of your own, ~/.satl/config.ini: one key = value a line, with # for a comment. The rows satl reads from it today include float.whole and float.decimal, the digits a float keeps before and after its point, directory.default, the folder satl’s own console starts in, and the switch access.

config.iniTEXT Download
~/.satl/config.ini: a comment, then two settings
# keep 64 digits after a float's point instead of 128
float.decimal = 64
access = true

Save the file, and the next program you run reads args.float.decimal as 64 and keeps floats to 64 digits. A missing config.ini is fine: satl prints a notice, S010, and uses its built-in settings. satl --rebuild writes one for you.

access is the one setting a program can also write: args.access = satellite.bool.false rewrites that row in your config.ini, and leaves every other line of the file as it was. That change lasts after the program ends, so only write it when you mean it. HISTORY AND ACCESS says what the switch is for.

Where next

Every row satl holds is listed in the FULL REFERENCE. For how to start satl with words after the file name, and its other options, see THE SATL COMMAND.

HISTORY AND ACCESS

THE SATELLITE CORPORATION

DATE:
ID: 17/42

USERNAME: Claude Code
CATEGORY: SATELLITE 004

satl’s own help lists two topics that let you look at your program’s names: satellite.access, “on by default, type/value for last instance”, and satellite.history, “off by default, record of everything”. This post shows where each stands in satellite 004. satellite.access(name) works. satellite.history(name) is not built yet, and satl refuses it. Both have switches, settings that say whether each one is on or off.

Asking about a name: satellite.access

satellite.access(name) tells you what a name is, what it holds now, and the exact code to reach into it, worked out from the name’s declaration. A line that is nothing but satellite.access(name) prints it. Anywhere else it answers the same text as a string, so satellite.console.display(satellite.access(name)) prints it too, and a program can keep it.

ask_access.satlSATELLITE 004 Download
What a name is, what it holds, and how to reach into it
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number score = 10
    satellite.access(score)

    satellite.container.map<satellite.variable.string, satellite.container.list<satellite.variable.number>> scores = {"ann": {7, 9}, "bo": {4}}
    satellite.access(scores)
    satellite.return(satellite)
}
OUTPUT
score is a number
  value  10
scores is a map (string -> list of numbers), 2 keys
  value                    {"ann": {7, 9}, "bo": {4}}
  scores["key"]            a list of numbers, "key" a string, one of "ann", "bo"
  scores["key"][n]         a number, n counting from 1
  scores["key"] = {1}      puts a list of numbers under "key"
  scores["key"].append(1)  adds a number
  scores.size              how many keys it holds
  scores.keys              the keys of it, as a list

For a container it shows one line for every level you can reach, with n, m and so on standing for positions (which count from 1) and "key" for a key. Then it shows a line that fills each level, with a value of the right shape written out, and .size and .keys. Every one of those lines is real code: satl’s own tests run each line satellite.access prints, for thousands of container shapes. For an object of a spacesuit, it shows the object’s fields and their values, and the capsules you can call on it. The name must be one the line can see; anything else in the brackets is refused before the program runs. ANY CONTAINER IN ANY CONTAINER uses it on deeper shapes.

Asking about the past: satellite.history, not built yet

satellite.history(name) is refused before your program runs. satl checks the whole file first, so nothing is printed before the report.

ask_history.satlSATELLITE 004 Download
satellite.history(name) is refused today
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number score = 10
    satellite.console.display(satellite.history(score))
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S110: LINE_NOT_UNDERSTOOD
satl(check): in satellite.main, no capsule named history

directory: ask_history.satl:6
syntax: satellite.console.display(satellite.history(score))
        /\

satellite has no scenario for this line -- it is spelled in a shape the language
does not have a meaning for.
machine code 13 satl_line_not_understood -- satl exits with this.

--------------------------------------------------------------------------------

The access switch

The access switch is built, and a program can read it. Its full name is satellite.library.main.arguments.access. It is a word the language owns, so it starts with satellite. It is also a row of the arguments variable, so a main that asks for that variable can read it short, as args.access. It is true unless you have turned it off.

access_switch.satlSATELLITE 004 Download
Read the access switch, by its full name and as a row
satellite.include(satellite)

satellite.capsule satellite.main(satellite.variable.arguments args)
{
    satellite.console.display(satellite.library.main.arguments.access)
    satellite.console.display(args.access)
    satellite.statement.if (satellite.library.main.arguments.access)
    {
        satellite.console.display("access is on")
    }
    satellite.return(satellite)
}
OUTPUT
true
true
access is on

The switch lives in your settings file, ~/.satl/config.ini, as the row access = true or access = false. With no row, it reads true. A program can write it too: satellite.library.main.arguments.access = satellite.bool.false rewrites that row and leaves every other line of the file as it was. Only satellite.bool.true and satellite.bool.false are accepted. Writing it is a lasting change to your settings, not just to one run.

The switch is for a store that is not built yet: the last value of every name, kept so you could ask about it after the program has stopped. Until that store exists, the switch changes nothing, and satellite.access(name) works whatever it says.

The history switch

History’s switch is a row in the same file, history = true or history = false. A missing row means false. A program cannot read or write it yet: reading satellite.library.main.arguments.history stops with “there is no value here to work with”, and writing it is refused before the program runs.

satl –rebuild and the feature register

What satl acts on when it starts is not those rows but one line in the same file that sums up every switch, features = b.... satl --rebuild writes that line from the rows. Run it after you change a switch by hand:

BASH Download
Fold the switches in config.ini into one line
satl --rebuild

It prints a table of every switch, whether it is on, and whether it is built yet. With access = true and history = true in config.ini, it prints, among its other lines:

OUTPUT
    bit  feature              state     built  what it does
    ---  -------------------  --------  -----  ------------------------------------
      0  access               ON        yes    keep the last known name, type and value of everything
      1  history              ON        NO     write every value a name ever held to disk

    turned on but NOT BUILT YET, so nothing will happen:
        history

The “yes” beside access means the switch itself is built; the store it will turn on is not. If you change a row and forget to run satl --rebuild, every run prints a notice, S011 REGISTER_IS_STALE, and keeps using the old line until you do. Nothing breaks: it is a reminder.

Where next

The FULL REFERENCE marks what is built and what is not. The arguments variable, which carries the access switch as a row, is in THE ARGUMENTS VARIABLE, and help on any part of the language is in THE HELP SYSTEM.

THE HELP SYSTEM

THE SATELLITE CORPORATION

DATE:
ID: 16/42

USERNAME: Claude Code
CATEGORY: SATELLITE 004

satellite carries its own help. You ask for it at the satl prompt, the place where you type one line and see it run straight away, and it answers with a page of plain text about one part of the language: what it is, the rules, and an example program.

Opening the prompt and asking

Start the prompt with satl --repl. Then type satellite.help() on a line of its own to see every topic there is:

BASH Download
Start the satl prompt
satl --repl
TEXT Download
Then type, at the prompt
satellite.help()
OUTPUT
--------------------------------------------------------------------------------
 THE SATELLITE HELP SYSTEM
--------------------------------------------------------------------------------
 usage: type satellite.help(include) or satellite.help(satellite.include)
 to bring up info on including other files
TOPICS:

1) satellite.include (other files)

2) satellite.help (the help system)

3) satellite.capsule (functions)

The list goes on through 28 topics, from numbers and strings to spacesuits, windows and the terminal. To read one, put its name between the brackets. You can name a topic in several ways:

  • by its whole name: satellite.help(satellite.include)
  • by its last part: satellite.help(include) finds the same topic
  • by a second spelling: satellite.help(colour) finds satellite.variable.color, and satellite.help(double) finds satellite.variable.float
  • by the start of its last part: satellite.help(percentage) finds satellite.variable.percent

satellite.help(help) brings up the help system’s own page. exit, quit or Ctrl-D leaves the prompt.

When a name fits two topics, or none

Two topics end in window, so satl will not guess which one you meant. It names both and lets you choose:

TEXT Download
Asking for a topic two topics answer to, and one that does not exist
satellite.help(window)
satl(prompt): window is more than one topic -- write satellite.help(satellite.variable.window) or satellite.help(satellite.window)
satellite.help(unicorns)
satl(prompt): there is no help on unicorns yet -- satellite.help() lists the topics there are

Help is for the prompt, not for a program

satellite.help is something you ask satl, not a statement your program runs. Written inside a program, it is refused before the program starts:

ask_for_help.satlSATELLITE 004 Download
satellite.help inside a program is refused
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.help(library)
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S210: NOT_BUILT_YET
satl(check): in satellite.main, satellite.help has no library built for it yet

directory: ask_for_help.satl:5
syntax: satellite.help(library)
        /\

this word is numbered in the language and the library behind it does not exist
yet. The word is right; there is nothing yet to run.
machine code 14 not_built_yet -- satl exits with this.

--------------------------------------------------------------------------------

At the prompt it also has to be the whole line: satellite.console.display(satellite.help()) is refused, and satl says that help is written on a line of its own.

Every topic ends in a program you can run

Each topic finishes with an example. The examples are real programs: save one as a .satl file and run it with satl. This is the example from satellite.help(library):

rounds.satlSATELLITE 004 Download
The example program from the library topic
satellite.include(satellite)

satellite.library.rounds = 4
satellite.library.label = "rounds counted"

satellite.capsule satellite.main()
{
    satellite.variable.number count = 0
    satellite.statement.while (count < satellite.library.rounds)
    {
        count = count + 1
    }
    satellite.console.display(satellite.library.label)
    satellite.console.display(count)
    satellite.return(satellite)
}
OUTPUT
rounds counted
4

Comments in an example, such as // 42, often show what a line prints, so you can check your run against the page.

Help is plain text, and it is yours

Every topic is a plain text file, read at the moment you ask for it and never built into satl. That means you can open them, read them in any editor, and change them. The next time you ask, you get your version.

The files live in a folder named satellite.help:

  • satellite.help/help.txt is the list that satellite.help() prints.
  • Each topic is a folder named after it, holding a file called help_text.txt. The include topic is satellite.help/satellite.include/help_text.txt.
  • A new folder is a new topic. Make satellite.help/my_notes/help_text.txt, and satellite.help(my_notes) shows it. Add a line to help.txt if you want it in the list too.

satl looks for the satellite.help folder beside the satl program, or one folder up from it. If it finds neither, it tells you so and names both places it looked:

TEXT Download
What the prompt says when the help folder is not beside satl
satellite.help(include)
satl(prompt): the help files are not beside satl -- looked in ~/.satl/satellite.help and ~/.satl/../satellite.help

Where next

The FULL REFERENCE collects the whole language on one page. For the prompt and satl’s other options, see THE SATL COMMAND. The library example above is explained in SATELLITE.LIBRARY.

THE SATL COMMAND

THE SATELLITE CORPORATION

DATE:
ID: 15/42

USERNAME: Claude Code
CATEGORY: SATELLITE 004

satl is the satellite interpreter: the program that reads a .satl file and runs it. Most of the time you only need one way to start it, satl hello.satl. This post covers the rest: the banner, the words you can hand a program, the prompt, satl’s own console window, and the options that do other jobs.

Running a program

Give satl the file, and it checks the whole program and then runs it:

hello.satlSATELLITE 004 Download
The program the rest of this post runs
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.display("hello")
    satellite.return(satellite)
}
OUTPUT
hello

In a terminal, satl hello.satl shows four lines before hello. This is the banner:

TEXT Download
The banner satl prints as it starts
THE SATELLITE PROGRAMMING LANGUAGE
VERSION 004 REVISION 08 BUILD 0043
CLANG++ 24 ALMALINUX 10.2
---------------------------------------------------------------

The second line is the version, the revision and the build number of the satl you are running. The third names the compiler that built it and the system it was built on, so yours may read differently.

The banner goes to the terminal’s error stream, not to its normal output. Your program’s output stays exactly what your program printed, which matters when you save it to a file or hand it to another program. To see the output alone, send the error stream away:

BASH Download
Run hello.satl without the banner
satl hello.satl 2> /dev/null

Handing words to a program

Everything after the file name belongs to the program, even words that look like satl’s own options. The program reads them through the arguments variable.

echo_words.satlSATELLITE 004 Download
Show every word the program was given
satellite.include(satellite)

satellite.capsule satellite.main(satellite.variable.arguments args)
{
    satellite.console.display(args.program + " was given " + (args.length - 1).to_string() + " words")
    satellite.statement.for(satellite.variable.number i = 1; i < args.length; i++)
    {
        satellite.console.display(args["argument_" + i.to_string()])
    }
    satellite.return(satellite)
}
OUTPUT
echo_words.satl was given 0 words
BASH Download
Two words that look like options go to the program
satl echo_words.satl --version --debug
OUTPUT
echo_words.satl was given 2 words
--version
--debug

Every option

satl --help lists these, and it is the one to trust if this page and your satl ever disagree:

  • satl on its own prints the banner and three lines on how to start.
  • satl <file.satl> [words...] runs a program, handing it the words.
  • satl --run <file> [words...] does the same, for a file whose name begins with -.
  • satl --debug <file.satl> [words...] runs it and shows every state and every argument as satl starts.
  • satl --repl opens the prompt: type a line, see it run.
  • satl --console [file.satl] [words...] does the same in a console window of satl’s own: the program, or with no file, the prompt.
  • satl --rebuild folds the switches in your ~/.satl/config.ini into the one line satl reads when it starts.
  • satl --config [most] measures, once, what this machine can do, such as how many threads it can hold, and keeps the answer for later runs.
  • satl --feedback shows what satellite.feedback has kept on this machine.
  • satl --license [n|name|all] shows the licences: satellite’s own, and those of the projects built into satl. --licence, --licenses and --licences work too.
  • satl --version or -V shows the version, revision and build.
  • satl --help or -h shows the list.

--debug and --console come before the file, --run or --repl. --version and --help must be the whole command line. A word satl does not know is refused, and it names the word:

TEXT Download
A mistyped option
satl --rum hello.satl
[satellite] satl(command line): "--rum" is not a word satl takes (a file whose name begins with - runs as satl --run --rum) -- satl --help lists every way to start satl (machine_code: 23 command_line_not_understood)

How a run ends: the exit status

When a program runs to its end, satl exits with 0. When something stops it, satl prints a report and exits with the report’s machine code, so a script that runs satl can tell what happened.

stop.satlSATELLITE 004 Download
A program that stops, and the code satl exits with
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.container.list<satellite.variable.number> scores = {7, 9}
    satellite.console.display(scores[3])
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S501: LINE_PAST_THE_END
satl(run): scores[3]: there is no such item -- the list holds 2 items, counting
from 1

directory: stop.satl:6
syntax: satellite.console.display(scores[3])
                                        /\

a line was read by its number and the file has no line with that number. Lines
count from 1, and the last one is the file's size.
machine code 47 line_past_the_end -- satl exits with this.

--------------------------------------------------------------------------------

The last line of the report says it: machine code 47. In a terminal, echo $? straight afterwards prints 47. A code that does not fit an exit status is written in full and satl exits 255. READING AN ERROR REPORT goes through a report line by line.

The prompt

satl --repl opens the prompt. It greets you with one line of instructions, then shows three things in square brackets, satellite, your user name and the folder you are in, followed by >>. Type a statement and press Enter, and it runs at once:

TEXT Download
A short session: each line typed at the prompt, and what satl answers
satellite.console.display(6 * 7)
42
run hello.satl
THE SATELLITE PROGRAMMING LANGUAGE
VERSION 004 REVISION 08 BUILD 0043
CLANG++ 24 ALMALINUX 10.2
---------------------------------------------------------------

hello
exit

The prompt runs a statement a line, and keeps what you declare until you leave: a variable declared on one line is there on the next.

It takes a whole block too. Type a line that opens one — satellite.capsule twice(satellite.variable.number n), satellite.statement.if(x > 3), a while or a for — and press Enter, and the prompt writes the { for you and starts the next line one step in. Type the body, then } (it steps back under the line that opened the block). A spacesuit gets more: satellite.spacesuit dog() and Enter writes its {, satellite.constructor() and the constructor’s {, and after each } you type it writes the next section, satellite.protected and then satellite.public, each with its {. What a block declares — a capsule, a spacesuit — is kept for the session, so later lines can call it or make objects of it. An if, a while or a for runs as soon as its last } is typed; an if waits for one more line, in case it is an else.

TEXT Download
Declaring a capsule at the prompt, and calling it
satellite.capsule twice(satellite.variable.number n)
... {
...     satellite.return(n * 2)
... }
satellite.console.display(twice(21))
42

run hello.satl, or interpret hello.satl, runs a whole program just as satl hello.satl would, banner and all. exit, quit or Ctrl-D leaves. The prompt is also where you ask for help: see THE HELP SYSTEM.

satl’s own console window

satl can open a console window of its own, so it works even where there is no terminal. satl --console hello.satl runs the program in that window, and satl --console with no file opens the prompt there.

satl also opens its console by itself when it is started with no terminal to write to, for example by double-clicking a .satl file or choosing satellite from a menu, as long as there is a screen to show a window on. If the output is going to a file or into another program, satl writes there instead and opens nothing.

When a program ends cleanly, its console closes. When it stops on an error, the window stays open with the report, and a last line says which machine code it stopped on and to press any key to close. A prompt session in the console ends the same way, with “the session is over — press any key to close”.

The options that print something and exit, --rebuild, --config, --feedback and --license, do not take --console: run them in a terminal. A script or a launcher that must never get a window can set the environment variable SATL_NO_WINDOW=1, and satl then never opens a console by itself.

Where next

The FULL REFERENCE lists everything satl understands. The words a program is handed are covered in THE ARGUMENTS VARIABLE, and installing satl is on the INSTALL page.

DISPLAYING TEXT

THE SATELLITE CORPORATION

DATE:
ID: 14/42

USERNAME: Claude Code
CATEGORY: SATELLITE 004

Almost every program you write will show something to the person running it. In satellite that is one word: satellite.console.display. It writes one line to the screen, and it is the first word most people learn.

The name follows satellite’s one big rule. A dotted path that starts with satellite names something the language owns: satellite.console is the console, and .display is what it does. A bare name, like apples or greeting, is something you own.

One value, any kind

satellite.console.display takes one value in its brackets and shows it on a line of its own. The value can be any kind: text is shown as it is, a number as its digits, a comparison as true or false, and a list with its curly brackets and commas.

show_anything.satlSATELLITE 004 Download
Display shows every kind of value
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number apples = 4
    satellite.console.display("hello")
    satellite.console.display(apples)
    satellite.console.display(apples + 1)
    satellite.console.display(apples > 3)
    satellite.console.display(3.5)
    satellite.console.display(50%)
    satellite.console.display({1, "two", 3})
    satellite.console.display("")
    satellite.console.display("an empty string makes an empty line")
    satellite.return(satellite)
}
OUTPUT
hello
4
5
true
3.5
50%
{1, "two", 3}

an empty string makes an empty line

Notice that apples + 1 is worked out first, and then the answer is shown. Whatever you put in the brackets is a value, and display shows the value.

Staying on the same line with end=

Each display ends its line, so the next display starts on a new one. When you want to keep going on the same line, add end= after the value. What you give it is written instead of the line ending. end="" writes nothing at all, and end=" " writes a space.

countdown.satlSATELLITE 004 Download
end= keeps the next display on the same line
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.statement.for(satellite.variable.number i = 5; i > 0; i - 1)
    {
        satellite.console.display(i, end=" ")
    }
    satellite.console.display("lift off!")
    satellite.console.display("Loading", end="...")
    satellite.console.display(" done")
    satellite.return(satellite)
}
OUTPUT
5 4 3 2 1 lift off!
Loading... done

The countdown’s numbers share one line because each ends with a space instead of a line break. The last display, "lift off!", has no end=, so it finishes the line.

Several values on one line

Display takes exactly one value. To show several things together, join them into one string with + first. Satellite never converts a value behind your back, so a number joins text only once you turn it into text yourself, with .to_string(). The other way is several displays, each with end=, and a last one to finish the line.

introductions.satlSATELLITE 004 Download
Two ways to put several values on one line
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.string name = "Ada"
    satellite.variable.number age = 36
    satellite.console.display(name + " is " + age.to_string() + " years old")
    satellite.console.display(name, end=" is ")
    satellite.console.display(age, end=" years old")
    satellite.console.display("")
    satellite.return(satellite)
}
OUTPUT
Ada is 36 years old
Ada is 36 years old

If you hand display two values separated by a comma, satl refuses the program before a single line runs, and tells you why:

two_values.satlSATELLITE 004 Download
display takes one value, not two
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.display("one", "two")
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S110: LINE_NOT_UNDERSTOOD
satl(check): in satellite.main, satellite.console.display takes one argument,
and was given 2

directory: two_values.satl:5
syntax: satellite.console.display("one", "two")
        /\

satellite has no scenario for this line -- it is spelled in a shape the language
does not have a meaning for.
machine code 13 satl_line_not_understood -- satl exits with this.

--------------------------------------------------------------------------------

The same goes for joining text and a number without .to_string(): that is refused too, with S301, rather than guessed at.

Things worth knowing

  • \n is a new line inside a string, and \t a tab: "one\ntwo" shows two lines. The six escapes are \" \\ \n \t \r and \'; any other backslash stays in the text as you wrote it. A separate display, or end=, controls lines too.
  • Your output is yours alone. The title block satl prints when it starts goes to standard error, and what your program displays goes to standard output. So you can save exactly what your program printed into a file:
BASH Download
satl countdown.satl > countdown.txt
  • Display can add colour and style. After the value it also takes foreground=, background=, bold= and italic=. They are covered in COLOR ON THE CONSOLE. Into a file or a pipe, like the one above, the text always goes plain.

Where next

Every option display takes is listed in the FULL REFERENCE. To let the person answer back, read READING INPUT, and for the text itself, STRINGS.

READING INPUT

THE SATELLITE CORPORATION

DATE:
ID: 13/42

USERNAME: Claude Code
CATEGORY: SATELLITE 004

A program that only talks is half a conversation. satellite.console.input is the other half: it waits for the person at the keyboard to type a line and press Enter, then hands that line to your program as a string.

Asking a question

Put a prompt in the brackets, and satl writes it with no line break after it, so the cursor waits right beside your question. The answer comes back as a satellite.variable.string, without the Enter at the end. Leave the brackets empty, satellite.console.input(), and it waits without writing a prompt.

This program waits for the keyboard, so it has no output box:

greet.satlSATELLITE 004 Download
Ask for a name and answer it
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.string name = satellite.console.input("What is your name? ")
    satellite.console.display("Hello, " + name + "!")
    satellite.return(satellite)
}

At a terminal, typing Ada and pressing Enter looks like this:

TEXT Download
What is your name? Ada
Hello, Ada!

The prompt also takes the same style options as display: foreground=, background=, bold= and italic=. So satellite.console.input("Your move? ", foreground=xFF8800, bold=satellite.bool.true) asks in bold orange. COLOR ON THE CONSOLE has the details.

Turning an answer into a number

What a person types is always text, even when it is digits. Satellite never converts a value behind your back, so "41" + "1" joins two strings into "411". To do arithmetic, read the number out of the text first with .number:

text_to_number.satlSATELLITE 004 Download
Text is text until you ask for its number
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.string typed = "41"
    satellite.console.display(typed + "1")
    satellite.variable.number n = typed.number
    satellite.console.display(n + 1)
    satellite.return(satellite)
}
OUTPUT
411
42

.number reads a whole number: an optional minus sign, then digits. Anything else is refused rather than guessed at, and the report says what happened:

not_a_number.satlSATELLITE 004 Download
Words are not a number
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.string typed = "forty-two"
    satellite.console.display(typed.number)
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S120: NUMBER_NOT_READ
satl(run): typed.number: that text is not a whole number this can read

directory: not_a_number.satl:6
syntax: satellite.console.display(typed.number)
        /\

this is not a number satellite can read. A number is an optional '-' then
digits, with no spaces or separators, and a float has one point in it: 12.34.
machine code 3 int_error -- satl exits with this.

--------------------------------------------------------------------------------

Putting both together, you can read the number straight off the call. Again, this one waits for the keyboard:

add_two.satlSATELLITE 004 Download
Add two numbers the person types
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number first = satellite.console.input("first number: ").number
    satellite.variable.number second = satellite.console.input("second number: ").number
    satellite.console.display("together they make " + (first + second).to_string())
    satellite.return(satellite)
}

Input from a file or a pipe

satellite.console.input reads standard input, so the lines do not have to come from a keyboard. You can pipe them in, which is handy for testing a program without typing the same answers again and again:

BASH Download
printf '12\n30\n' | satl add_two.satl
OUTPUT
first number: second number: together they make 42

The prompts run together here because nobody typed anything: at a terminal, your own typing and the Enter key are what move the cursor to the next line.

When the input runs out

A piped file ends, and a person can press Ctrl-D to say “no more”. Either way there will never be another line, so satl stops with S830 INPUT_ENDED and exits with 55. It does not answer an empty line forever. Here is greet.satl again, run with nothing to read, as it is when you run satl greet.satl < /dev/null:

greet_nothing.satlSATELLITE 004 Download
With nothing to read, input stops the program
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.string name = satellite.console.input("What is your name? ")
    satellite.console.display("Hello, " + name + "!")
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S830: INPUT_ENDED
satl(run): in name = ..., satellite.console.input(prompt) waited for a line, and
the input has ended

directory: greet_nothing.satl:5
syntax: satellite.variable.string name = satellite.console.input("What is your name? ")
                                                                                       /\

satellite.console.input() waited for a line and the input has ended -- a file
piped in ran out, or Ctrl-D was pressed. There will never be another line, so
this stops rather than answering an empty one forever.
machine code 55 input_ended -- satl exits with this.

--------------------------------------------------------------------------------

An empty line is different: a person pressing Enter on its own gives your program the empty string "", and the program carries on.

Where next

Every console word is in the FULL REFERENCE. To keep asking until the person is done, see IF, WHILE AND FOR, and for what you can do with the text you get back, STRINGS.