A collection is a data structure that stores multiple values. Acorn's core library offers these collection types:

Additional collection types can also be defined, as needed.

The Acorn language offers unique facilities to simplify snapping collections together into rich static or procedural content:

'this' and its block

A 'this' block is simply an expression followed by a code block. It is used to focus the attention of a block of code on one specific value (referred to by this). Within the block, multiple actions may be performed using that value.

# Calculate the value of 'this' (0.174533)
Float.Pi/180
	# 'this' block that makes use of calculated 'this' value
	# to convert from degrees to radians
	quarter = 90 * this
	acute = 10 * this

'this' blocks can be nested within each other. For any statement, the value of this is established by the inner-most 'this' or method block it lies within. Every method block sets the value of this to be equivalent to self. Control blocks have no impact on the value of this.

Although a 'this' block can focus on any value, it is most often a newly created or existing collection.

'this' operators

A number of Acorn's operators are designed to work with this by default. For example, all of the following operators assume that 'this' is implied on the left when the operator is used as a prefix (rather than its typical infix role):

.
Method call or property access. (.size is equivalent to this.size)
.:
Property access. (.:size is equivalent to this.:size)
::
Indexed access using a literal symbol. (::size is equivalent to this::size)
<<
Append, placing the value at the end of an ordered collection. (<<4.7 is equivalent to this<<4.7)
>>
Prepend, inserting the value at the start of an ordered collection. (>>event is equivalent to this>>event)

Additionally, the ':' and ':=' infix operators implicitly work with 'this':

color: 'brown'    # equivalent to: this.color = 'brown'
action:= [x]      # equivalent to: this::action = [x] {x*x}
	x*x

List example

Ordered lists are easily populated:

# Create an ordered list
letters = +List 
	<< 'b'
	<< 'c','d','e' # all comma-separated or return values are appended
	>> 'a'
# letters has the values: 'a', 'b', 'c', 'd', 'e'

The 'using' variation of the 'this' block may be used to automatically append the value of every indented statement (using <<) to 'this':

+List using '<<'
	'a'
	'b'
	'c', 'd', 'e'

Additionally, the 'using' clause may specify a method to use on 'this' for every indented line's value:

list = +List using [x] {<< x*x}
	1;2;3
list   # +List(1,4,9)

Text example

Textual content may be built similarly to Lists:

Vm using 'Print'
	"The answer is ";answer;".\n"

Index example

The ':=' operator makes it easy to populate an Index with keyed values:

toDutch = +Index
	girl:= 'meisje'
	horse:= 'paard'
	dog:= 'hond'

We cannot use the ':' operator to populate an Index, since the ':' operator sets properties and an Index's keys are not properties. As with ':', when ':=' immediately follows a name token, it is treated as a symbol. To ensure a name is treated as a variable, simply enclose it in parentheses.

Type example

Types are built similarly to Index collections. See the Type Definition chapter for examples.

Iteration and 'each'

Iterators

An iterator is a stateful method (closure or yielder) which can be used to retrieve values one at a time from a sequence of values. Since we want an iterator to be able to tell us when it is done, an iterator returns two values (as a general convention): the first is null when done and the second is the desired value.

For example:

counter = []
	return null, null if count>=2
	count = count + 1
	true, count

counter() # true, 1
counter() # true, 2
counter() # null, null

Iterators are used "lazily". They only retrieve one value at a time when requested and do not know what the next value will be until asked to get it.

Typically, an iterator is single-use: it is created just before it is needed, used to retrieve values in a single pass, and then discarded afterward as its state makes it now useless to us, given that it will only ever afterward return null, null.

'Each' method

Several of Acorn's core types (such as Range, Text, List, Index and Type) provide a method called 'Each'. When applied to an instance of these types, the 'Each' method creates and returns an iterator (typically as a closure). This iterator can then be used to retrieve one value at a time from the sequence of values defined by that instance.

For example:

counter = +Range(1,2).Each
counter() # true, 1

# Text iterator will return each character
chars = "abc".Each 
chars() # 1, "a"

mbrs = +List('abc', 4)
mbrs() # 1, 'abc'

mbrs = +Index {key: 'value'}
mbrs() # 'key', 'value'

Please notice the first value returned by these iterators: it is the (never null) index of that value within the instance's sequence. Additionally, please notice that if an instance's sequence of values are ordered, the iterator retrieves them in order. If not (as is true for an Index), the iterator may well return the values in a different order each time.

'each' blocks

An 'each' block is like a 'while' block. They both repetitively perform a block of code. However, an 'each' block is given an iterator rather than a condition. It executes that block of code for each value it retrieves from that iterator. It stops when the iterator is done.

For example:

# Builds a list containing 1, 4, 9, 16
+List
	each x in +Range(1,4).Each
		<< x*x

Every 'each' block specifies three things:

'break' and 'continue' statements work inside 'each' blocks the same way they do in 'while' blocks.

As you can see, 'each' blocks, working in conjunction with corresponding 'this' blocks, provide a simple but powerful mechanism for building procedurally-generated collections (essentially accomplishing the work of a comprehension). 'each' blocks can also be very effective tools for analyzing, summarizing or transforming complex content.

The index value You may have noticed that even though iterators return two values, the sample code only demonstrated access to the second value. The 'each' actually does look at the first value under the covers, as it stops performing the block of code as soon as the iterator returns a first value of 'null'.

If you want your 'each' block's code to see the first value returned by the iterator (since it is often the collection's index for the iterator's retrieved value), just ask for it using two variable names separated by a colon:

# this code is equivalent to: found = dragon('color')
found = null
each key:value in dragon
	found = value if key='color'

Some iterators may return more than two values. Specify additional variable names separated by commas to access the remaining returned values.

Implicit iterator creation

The 'each' block attempts to make an iterator out of any value you give it:

Thus, we can omit specifying .Each for ranges and collections:

+List
	each x in 1 .. 4     # since 1 .. 4 means the same as +Range(1,4)
		<< x*x

Similarly, 'each' easily handles the creation, initialization and use of a yielder context:

fibonacci = *[start=1, inc=0]
	local i = 1
	while true
		yield i, start
		start, inc, i = start+inc, start, i+1

# Prints: 1: 2, 2: 6, 3: 8, 4: 14, 5: 22, 6: 36, 7: 58, 8: 94, 9: 152
each i:n in fibonacci(2, 4)
	Vm using 'Print'
		i.Text; ": "; n.Text;
	break if n>100
	Vm.Print(", ")
Vm.Print("\n")

This intelligence makes the code more concise for the common scenario of iterating through a collection. However, it also improves flexibility, as it allows one to use other methods than 'Each' for iterating through a collection. For example, a collection might offer the 'ReverseEach' method that returns a closure which iterates through that ordered collection in reverse order. Specifying each x in list.ReverseEach would make use of that iterator.

Splat (...) The 'each' block also allows you to specify '...' instead of an iterator. This will generate an iterator that provides sequential access to the ordered variable parameter values referred to by '...'. For example, the following code aggregates the variable parameters into a List (should one want random access to its values)

+List
	<< val each val in ...

'each' clause

Much like 'while', an 'each' block may be specified using a clause that follows a statement:

# Produces the list: 1, 9, 16
+List
	<< x*x if x!=2 each x in 1 .. 4

Notice we included an 'if' clause as well, to filter out specific values returned by the iterator. A statement may be given multiple clauses. They are evaluated in reverse order. Thus, the above code which uses two clauses is equivalent to:

+List
	each x in 1 .. 4
		if x!=2
			<< x*x

The following example demonstrates use of nested 'each' blocks, both expressed as clauses:

pairs = +List
	<< +List(x, y) for y in 3 .. 6 for x in 1 .. 6

Finally, the following more-complex example defines an inefficient method that builds and returns a calculated list of prime numbers. Notice that it also makes use of nested 'each' blocks, where the values iterated over in the inner block depend on the values iterated by the outer block.

calcPrimes = [upto]
    mults = +List
    +List
        each i in 2 .. upto
            if !mults.Find(i)
                << i
                if i<=upto.Sqrt
                    mults<<j each j in +Range(2*i, upto, i)

Iteration mediation: zips, joins and pipes

There are times one needs an iterator that obtains its values from multiple iterator sources. This can be accomplished by creating a closure or yielder that coordinates the handling of values retrieved from multiple iterator sources.

Zip For example, the language Haskell provides a zip comprehension capability. Here is code for an Acorn equivalent, implemented using a closure iterator:

# Haskell code: b = [(x,y) | (x,y) <- zip [1..5] [3..5]]
zipper = <iter1 = (1 .. 5).Each, iter2 = (3 .. 5).Each>[]
    done1, val1 = iter1()
    done2, val2 = iter2()
    done1||done2, +List(val1,val2)
+List
    <<i each i in zipper

This approach can be generalized to a reusable method that returns a zip iterator.

Combinatorial Join Haskell also offers a comprehension that performs a combinatorial join. This Acorn example accomplishes something similar using a yielder as the joining iterator:

# Haskell: a = [(x,y) | x <- [1..5], y <- [3..5]]
joiner = +[] [iter1, iter2]
    i2list = +List {<<i each i in iter2}
    yield   # the first invocation is just for set up
    each i in iter1
        iter2 = i2list.Each # re-start the iterator
        each j in iter2
            yield true, +List(i,j)
    null, null # signal that joiner iterator is done

joiner((1 .. 5).Each, (3 .. 5).Each) # do the set up
+List 
	<<i each i in joiner

This algorithm too can be generalized into a Joiner method that returns an iterator. The generalized method could even accept a first-class method that specifies how to join together the values from various iterators.

Acorn is quite flexible here, allowing creation of a wide variety of complex joining schemes. It could even support providing an SQL-like query that selects records from various sources (SELECT), joins them based on specified conditions (WHERE), and even orders the results.

Finally, Acorn supports the creation of piping iterators (using closures or yielders) which filter or transform values sourced from another iterator.

_