Once we have a value, a named property can be applied to it. The result depends on the requested property:

The properties available to a value are determined by the value's type. Thus, the properties that can be applied to an Integer value will be different than those which apply to a List collection.

This chapter describes several ways to make use of a value's properties or methods.

The '.' Operator

The '.' operator is used to access a value's properties or methods which it inherits from its type.

Property Access

Use the dot operator '.' to explicitly access a value's property. The base value goes to the left and the name of the property goes to the right. The name can be a name token, a symbol token, or an expression in parentheses that evaluates to a symbol. Acorn uses the property it finds within the value's type.

For example, collections (such as a List) often offer the 'size' property which corresponds to the number of values held by the collection. Thus:

# alphabet is a List containing the letters of the alphabet
alphabet.size    # returns 26

To change any property's value, place it to left of an assignment operator:

alphabet.size = 10   # The alphabet list is now smaller, only having the letters "a"-"j"

When no base value is placed before the dot, Acorn uses this as the base value. Refer to this blocks for a more detailed treatment on how several operators like '.' implicitly make use of the 'this' value.

.size    # equivalent to this.size

'null' is returned when using a property unknown to the value's type:

alphabet.mumble  # returns null

Calculated property names

Property names are nearly always symbols. Most of the time, we know which property we want to use by name, so we just specify that name after the dot. Acorn treats this variable-like identifier after the dot as a symbol, rather than a variable.

If the property name requires characters not allowed in a variable name, enclose its name within single quotes:

alphabet.'size'   # equivalent to: alphabet.size

For accessing a property whose name is dynamically specified, use an expression within parentheses that calculates the property's symbolic name:

shape.(attr)      # if attr is 'color', equivalent to: shape.color

Method Calls

Calling a method also uses the '.' notation, but may add to it comma-separated values in parentheses to pass on to the method as its parameters. The base value (to the left of the dot) is passed to the method as the implicit parameter self:

# Insert characters into a text
"pot".Insert(2, "u") # returns "pout"

Here is another example:

# For Floats, the '+' method adds the two numbers
4.0.'+'(2.1)      # Returns 6.1
# For text values, the '+' method concatenates them
"abc".'+'("def")  # Returns "abcdef"

Three notes about this example:

Return values from method calls

Most methods will return at least one value. The return value(s) can be ignored, if desired. If the requested method does not exist, 'null' is returned.

A method may return multiple values. One way to capture these values is with a parallel assignment:

a,b = list.Unpack  # a receives the first return value, b the second

Passing a Variable Number of Parameters

A value returned from one method may be passed on to another method. If a method call is used as the last parameter and it returns multiple values, all those values are included as parameters:

# Log a message using position's unpacked x, y, and z values.
$.Log("position at: %,%,%", position.Unpack)

Similarly, using the splat operator ('...') as the last specified parameter will include all its values as parameters.

# Passing on the remaining parameters received
$.Log("msg %", ...)

Method Assignment

Like properties, method calls may appear to the left of an assignment:

thing.Add('tweeter') = 'simple'

This only works if the Add method is implemented using a get/set Closure that has a defined 'set' method. The value to be assigned is passed to the method as the first parameter, followed by the specified parameters. Thus, the example above calls the Add closure's 'set' method, passing it the two parameters 'simple' and 'tweeter'.

Calculated Method Names

Just like with properties, the symbolic name of a method may be calculated using an expression enclosed within parentheses:

thing.(verbsym)(a,b)   # If verbsym is 'Add', equivalent to: thing.Add(a,b)

Alternatively, the expression may return an executable value (such as a method or closure) rather than a symbol. In this case, the method will be called directly (bypassing the step where the method to perform is retrieved using its symbolic name). This is helpful when calling anonymous methods.

self.(approach)(a,b)  # approach is a variable holding an anonymous method

Method Chaining

Method calls can be appended end-to-end. They are performed from left to right, with the return value of the first being used as the base value of the second, and so on.

# Handle when frosty is hit by the summer sun
frosty.Jump(10).Yell("Ow!").Grow(0.9)

Chaining depends on a convention where methods return 'self' when no other return value is expected. Thus, the value of frosty is effectively passed on to all three methods, making him jump, yell and shrink.

A similar technique, method cascading, is also supported using this blocks. Method cascading does not require that the cascaded methods return 'self'.

Computed Properties

So far, this chapter has presented properties and methods as distinctly different sorts of things. It is not quite that clear-cut. First of all, properties and methods share the same symbolic namespace as established by the base value's type. By convention, symbols that start with a lower case letter are typically properties. Capital-lettered symbols are typically methods.

How does Acorn know whether the dot operator is to access a property value or call a method? It doesn't look at the name, which is not enforced. If the inherited value assigned to that name refers to something executable it calls it. Otherwise, it treats the symbol's assigned value as a property to retrieve or change.

Computed properties are a hybrid between properties and methods. They look just like properties, as no parameters are specified. However, the computed property's result is performed by calling the property's defined method or closure.

For example, the '.size' property for collections is typically implemented as a computed property using a get/set closure.

The '.:' and '::' operators

The '.:' operator works exactly like '.' with one exception: Any executable value referred to by the inherited property or method name will not be called. Instead, that executable value (e.g., a Method) is returned as a first-class value. Using '.:' is the equivalent of treating methods (and computed properties) just like properties.

method = list.:size   # retrieves the method or closure that list uses to get/set its size

The '::' operator is similar to '.:', except it works with a value's owned (rather than inherited) values. It is used to retrieve or set a specific value within a collection using the specified index. As with '.', the identifier after '::' is treated as a symbol rather than a variable. Like '.', '::' operations can be chained.

capitals::a = 'A'
capitals::a  # 'A'
# Access Mr. Jones's barn's weather vane
farm::barn::vane

Method Call Shortcuts

The language features described above are sufficient to perform any kind of property access or method call. However, to help make code more succinct and readable, Acorn offers a number of useful shortcuts for certain kinds of method calls, such as:

Direct method calls

It is possible to call a method directly without using the dot notation. This is useful for calling locally defined anonymous methods or methods retrieved using the '.:' operator. To perform this call, follow the variable (or expression) that holds the method with parentheses that enclose all parameters. This call automatically assumes self to be the base value (unless the called method is a closure that has bound self to another value):

# Assume the variable 'add' holds a method which adds together its two parameters
add(4,5)   # returns the value of 9. Equivalent to self.(add)(4,5)

[] Indexing

Indexing provides direct get or set access to specified elements within a collection. To index into a collection, follow the collection's value with the desired index enclosed in square brackets. Under the covers, Acorn uses the collection's '[]' method to perform the indexing.

# Assume the variable 'letters' holds a List containing the values: 'a', 'b', 'c'
letters[0]        # returns the value 'a'. Equivalent to: letters.'[]'(0)
letters[1] = 'x'  # changes the second value in letters from 'b' to 'x'

# Assume the variable 'dict' holds an empty Index
dict['word'] = "A collection of letters."
dict['word']      # returns "A collection of letters."
dict['gnihton']   # returns null, as no value exists at that index

Note that dict['word'] will typically give the same result as dict.word. However, it does not have to: '[]' uses a method which can be overridden. By contrast, '::' is an internal operator whose logic cannot be altered.

Arithmetic and Other Operators

Several operators may be used as syntactic short-cuts for method calls, where the method's symbolic name is typically the same as the operator.

Arithmetic Operators

Acorn supports the standard set of arithmetic operators, each of which maps to a specific method name. They can be used with any type that defines methods for them, such as Integer, Float, and various collection types:

'+'
Addition or concatenation. When '+' is used as a prefix operator, it creates new instance values (see below).
'-'
Subtraction or elimination. When '-' is used as a prefix operator (negative), its method name is '-@'.
'*'
Multiply or replication.
'/'
Divide or split.
'%'
Modulo or format.
'**'
Exponent.

Acorn applies the normal algebraic precedence order for these operators. Parentheses can be used to override the order of evaluation.

a+b*c    # equivalent to a.'+'(b.'*'(c))
(a+b)*c  # equivalent to (a.'+'(b)).'*'(c)

Arithmetic Assignment

For convenience, Acorn does support the arithmetic assignment operators: '+=', '-=', '*=', '/='.

a.b(c) += 1   # equivalent to a.b(c) = a.b(c) + 1, with the values a,b,c only evaluated once

Although these assignment operators do not perform the assignment using a method, the arithmetic operation is performed using a method.

Collection Operators

In addition, Acorn offers these collection-only operators:

<<
Append operator. This adds the value on the right to the ordered collection on the left (similar to '+', but it changes the existing collection rather than creating a new one).
>>
Prepend operator. Similar to append, but it inserts the second value into the beginning of the ordered collection.

Comparison Operators

Several comparison operators are implemented using the '<=>' method. Additionally, the '~~' operator is implemented using a method of the same name. See the Conditional Expressions chapter for details on these operators.

'+' New Operator

The + prefix operator, followed by a type value, creates a new value (or instance) of that type. For example:

+List    # Creates a new empty List. Equivalent to List.New

Parameters, enclosed in parentheses, can be added to guide how the new value is initialized:

+List(1,2,3)  # Creates a new List that has three values

Alternatively, a literal symbol or string can be specified without parentheses, and will be treated as a single parameter:

+Regex"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b"

Some notes:

Range Constructor

The '..' operator is a useful shortcut for creating new Range values:

# The space before .. prevents confusing 1 with the Float 1.
1 .. 4        # equivalent to: +Range(1,4)
1 .. 5 .. 2   # equivalent to: +Range(1,5,2)

Range values are useful for iterating over a range of values or for matching whether a value falls within an interval of values.

_