Chapter 2

Lists

A program is a list of words, and so is everything a program keeps for later. This chapter is about making lists, taking them apart, and running them.

Writing a list

{ starts a list and } ends it. Everything between them is collected as it is, without running:

{ a b c }
values: ( ( "a" "b" "c" ) )

The list's first word is its top, and lists print top first, just like the values stack. Lists can hold lists:

{ a { b c } d }
values: ( ( "a" ( "b" "c" ) "d" ) )

The words inside are not run, and that includes #: { # 1 } is a list of two words, # and 1, not a list holding the number 1.

{ # 1 } size debug
2
values: ( )

Running a list

call takes a list off the values stack and puts its words on the instructions stack, so they run next. A list in braces is a piece of program you can keep, pass around and run later.

# 5 { dup * } call debug
25
values: ( )

Collecting values

[ and ] are the other way to make a list: the words between them do run, and whatever they leave on the values stack becomes the list.

[ # 1 # 2 # 3 # 4 + ]
values: ( ( 1 2 7 ) )

Compare { # 1 # 2 }, a list of four words, with [ # 1 # 2 ], a list of two numbers. [ marks the values stack and ] takes everything pushed since the mark, so values already on the stack before the [ are left alone. The first value pushed inside the brackets becomes the list's top, so the list reads in the order you wrote it:

before [ first second ]
values: ( ( "first" "second" ) "before" )

Taking lists apart

Stacks are written top first, as in chapter 1:

wordtakesleaves
size( list )( n ), how many elements
peek( list )( first )
pop( list )( first rest )
push( list v )( list′ ), the list with v first
reversed( list )( list′ ), backwards
append( b a )( list′ ), the elements of a and then of b
{ a b } { c d } append
values: ( ( "a" "b" "c" "d" ) )
{ a b c } pop debug debug
a
( "b" "c" )
values: ( )

pop leaves the rest of the list below the element it took, so the first debug prints a and the second prints what was left. push wants the value first and the list on top:

x { a b } push
values: ( ( "x" "a" "b" ) )

If the list is already on the stack and the value arrives after it, use swap first:

{ a b } x swap push
values: ( ( "x" "a" "b" ) )

Frozen and thawed

Every Ether object is either frozen, which means it never changes, or thawed, which means it can. A list in braces is a frozen list, called a tuple. stack makes a new, empty thawed list, called a stack. inspect shows what an object is without removing it.

{ a b } inspect del
stack inspect del
tuple frozen ( "a" "b" )
stack thawed clean cold serial 6 ( )
values: ( )

inspect prints the kind of object first (tuple or stack), then frozen or thawed. For thawed objects it adds three more things you can ignore for now: clean or dirty (whether it holds any thawed list or table), cold or warm (whether it holds any thawed object at all), and its serial, a number the process gives each thawed object it makes, in order.

Pushing onto a tuple never changes it: push gives back a new tuple and the old one is still there. Pushing onto a stack changes that stack, and every reference to it sees the change; push then gives back the same stack. Here dup makes a second reference to the same list before the push, and the push's result is thrown away with del:

{ a } dup x swap push del debug
stack dup x swap push del debug
( "a" )
( "x" )
values: ( )

The tuple is unchanged; the stack now holds x. Most of the time, frozen objects are the safer choice: nothing can change them behind your back, and they can be shared freely, even between processes. freeze makes a frozen copy of a thawed object, and clone makes a new, separate one.

stack x swap push freeze inspect
tuple frozen ( "x" )
values: ( ( "x" ) )

Exercise 2.1

Make a list of the numbers 10, 20 and 30, in that order from the top, and print its size.

A correct answer prints:

3
values: ( )
One answer
[ # 30 # 20 # 10 ] size debug
3
values: ( )

Exercise 2.2

Run the program { # 1 + } three times on the number 5, then print the result. Write the list only once: keep it on the values stack and use dup, swap and swap2 to have a copy on top, with the number under it, each time you call. Any other word from chapters 1 and 2 is allowed too. Writing the stack down after each word, top first, helps.

A correct answer prints:

8
values: ( )
One answer
# 5 { # 1 + } dup swap2 swap call swap dup swap2 swap call swap call debug
8
values: ( )

Exercise 2.3

Starting from { red green blue }, print red and then leave the list ( "green" "blue" ) on the values stack.

A correct answer prints:

red
values: ( ( "green" "blue" ) )
One answer
{ red green blue } pop debug
red
values: ( ( "green" "blue" ) )

Exercise 2.4

Make a stack, push red and then blue onto it, freeze it, and inspect the result.

A correct answer prints:

tuple frozen ( "blue" "red" )
values: ( ( "blue" "red" ) )
One answer
stack red swap push blue swap push freeze inspect
tuple frozen ( "blue" "red" )
values: ( ( "blue" "red" ) )