On this page:
5.1 Direct values and names
5.2 Ordered lists and positions
5.3 Records and fields
5.4 Numeric ranges
5.5 Membership and field presence
5.6 Kinds and interface tests
5.7 Operators and conditions
5.7.1 Equality and unknown
5.7.2 Boolean operators and precedence
9.3

5 Values and expressions🔗ℹ

Values can be written directly or obtained from a name. Expressions can be used in assignments, procedure calls, output, conditions, records, and ranges. The Syntax page defines the naming convention.

5.1 Direct values and names🔗ℹ

Form

Meaning

"seconds"

Quoted text

2

An exact number

0.25

An exact decimal number

true

Boolean true

false

Boolean false

unknown

An explicit value whose content is not known

:input

A declared input inside its procedure

name

A visible value, result, or record interface

Unquoted names refer to values; quoted text remains text. ScienceLogo keeps decimal literals exact. unknown is a value, so a record field containing it is present even though its scientific value is unknown.

5.2 Ordered lists and positions🔗ℹ

[1 2 3] is an ordered list. Lists can contain other values and lists; [[1 2] [3 4]] is a list of rows. To put a comparison in a list, group it with parentheses: [(x == y)].

sequence.[position] reads one list element. Positions start at 1, and the position must be an exact positive integer within the list. grid.[1].[2] reads the second value of the first row. Several positions, as in sequence.[1 3], select a new list in the stated order.

Use = on an indexed target to replace an existing list entry. Use += on the list itself to append one item:

Operation

Form

Effect

Replace an entry

values.[2] = 3

The second entry becomes 3; the list length stays the same

Append an item

values += 3

3 becomes a new last entry; the list length increases by one

Compact

values = [1 2]

values.[2] = 3 ; values is now [1 3]

values += 4 ; values is now [1 3 4]

values += [5 6] ; values is now [1 3 4 [5 6]]

Verbose

set values to [1 2]

set [2] of values to 3 ; values is now [1 3]

update values with 4 ; values is now [1 3 4]

update values with [5 6] ; values is now [1 3 4 [5 6]]

The appended value is one complete item, even when it is a list or record. To replace several existing positions together, use values.[1 = 7 2 = 8]. Lists remain lists from their first entry; += does not convert a scalar into a list.

sequence.count reports the number of items. .count is a reserved, read-only list property:

Compact

measurements = [2 unknown 2.1]

print measurements.count ; 3

print [].count ; 0

Verbose

set measurements to [2 unknown 2.1]

print count of measurements ; 3

print count of [] ; 0

It counts positions, so unknown item values still count. An unknown list has an unknown count. The property also works on nested lists, selected lists, and ranges, such as grid.[1].count or lengths.count. Use it in assignments, procedure inputs, arithmetic, loops, and conditions. Changing a list changes its count automatically. Assigning to measurements.count, including with +=, is an error. Requirements for every item in a list shows how an interface can require a minimum or exact count.

5.3 Records and fields🔗ℹ

Brackets whose first entry is name = value make a record with named fields. Field names are unquoted and distinct within one record:

Compact

trial = [

  duration = 2

  unit = "seconds"

]

print trial.unit

Verbose

set trial to [

  set duration to 2

  set unit to "seconds"

]

print unit of trial

trial.unit reads one named field. trial.[duration unit] selects a new record containing both named fields; it does not create local names. It is an error to read a missing field. Field order does not affect record equality. Brackets without an initial name = value make a list instead.

A record may have an ordinary field named count. For example, trial = [count = 7] makes trial.count equal to 7, and trial.count = 8 updates that field normally. The reserved list property applies when the value before .count is a list; it does not reserve count as a record field or variable name.

Data stored outside the workflow uses a declared Data sources and storage instead of an ordinary record path. An attached Uncertainty descriptions is read with value.u; u is not an ordinary user field name. In a record declaration, clock.time = "12:00" creates nested fields; it has the same shape as clock = [time = "12:00"].

trial.unit = "minutes" updates one field. At command position, trial.[unit = "minutes" duration = 3] updates or adds several fields. The equivalent update command trial += [unit = "minutes" duration = 3] also updates existing fields and adds missing ones. Its right side must be a record written with named fields. The name on the left must already exist. An earlier value assigned from trial retains its earlier contents. On the right side of an assignment, trial.[unit = "minutes"] makes a modified value for another name. An interface name can be used the same way to construct a record with its defaults and required fields; see Records and interfaces.

Compact

trial = [duration = 2 unit = "seconds"]

revised = trial.[unit = "minutes"]

trial += [unit = "minutes"]

trial.duration += 2

Verbose

set trial to [set duration to 2 set unit to "seconds"]

set revised to trial with [set unit to "minutes"]

update trial with [set unit to "minutes"]

update duration of trial with 2

Here revised keeps its earlier contents after trial changes. On a numeric value, += adds the number on its right: the last line changes trial.duration from 2 to 4. The numeric value must already exist and both values must be exact numbers. Nested fields use the same path, such as trial.clock.unit. trial.[clock.time = "12:00"] updates that nested field while preserving other fields of clock. Missing containing fields are created as records; an existing containing value must be a record. Propositions describes a separate += rule for adding named propositions under another proposition.

5.4 Numeric ranges🔗ℹ

range start to end step increment makes an ordered list of exact numbers. The end is included only when a step lands on it. The step must be nonzero and move toward the end. Bounds and step can be expressions:

Compact

lengths = range 0.25 to 1 step 0.25

for each length in lengths [print length]

Verbose

set lengths to range from 0.25 to 1 with step 0.25

for each length in lengths [print length]

One named range can be reused in several loops. Range values may start at zero; the one-based rule applies only to list positions.

5.5 Membership and field presence🔗ℹ

Use in for both checks. The value on the right determines what is checked:

Form

Meaning

value in list

Does the list contain an equal item?

field in record

Does the record contain that named field?

Compact

trial = [period = unknown method = "manual"]

if period in trial [print "period is present"]

if not (notes in trial) [print "notes is absent"]

Verbose

set trial to [set period to unknown set method to "manual"]

if is period in trial then [print "period is present"]

if not (is notes in trial) then [print "notes is absent"]

For a record, the left word is an unquoted field name. It need not name a variable, and a variable with the same name does not change which field is checked. Presence gives true even when the field’s value is unknown or false; a missing field gives false. Only the immediate fields are checked. Use a nested record on the right, such as notes in trial.details, to check a field inside it. Quoted text and other value expressions are not record field selectors.

value in list tests whether a value equals an item in a list:

Compact

methods = ["manual" "video"]

method = "manual"

accepted = method in methods

if accepted [print method]

Verbose

set methods to ["manual" "video"]

set method to "manual"

set accepted to is method in methods

if accepted then [print method]

The right side can be a literal list, a named list, a nested list field, or a range. With a list on the right, names on the left supply their stored values. Membership compares whole items using the same equality as ==; it does not search inside an item’s fields or nested lists. For example, 1 in [[1 2]] is false.

A matching item returns true; if every comparison is false, membership returns false. An empty list always gives false. The explicit unknown marker is compared like other items: unknown in [unknown] is true, while 2 in [unknown 1] is false. When the entire list or record is unknown, the check remains unknown. A checker unable to establish an operand can also leave the result undetermined. Any known right operand other than a list or record raises an error when run.

Membership works wherever expressions are accepted, including procedure inputs and outputs, conditions, and interface requirements. Use not (method in methods) to negate it. It has the same precedence as ==; comparisons cannot be chained. The runnable example uses both a named list and an interface requirement. The field-presence example shows both checks together.

5.6 Kinds and interface tests🔗ℹ

value is kind checks the outer kind of a value:

Compact

period = 2

numeric = period is number

if numeric [print period]

Verbose

set period to 2

set numeric to is period a number

if numeric then [print period]

The available kinds are number, text, boolean, list, and record. A record is an object with named fields. These words name kinds after is; they can still be ordinary names elsewhere. The is operator is reserved and has comparison precedence.

A known value gives true or false; unknown is number gives unknown. The test checks the outer value, so [unknown] is list is true. An attached u description does not change the kind. Combine tests with and, or, and not; comparisons cannot be chained. These expressions also work in procedure inputs and outputs, loops, and interface conditions.

The same is accepts a declared interface name:

Compact

interface Timing [must [period is number unit = "seconds"]]

trial = [period = 2 unit = "seconds"]

if trial is Timing [print trial.period]

Verbose

interface Timing [must [period is a number set unit to "seconds"]]

set trial to [set period to 2 set unit to "seconds"]

if is trial a Timing then [print period of trial]

This checks the interface’s requirements against the supplied object. It returns true for a match, false for an established mismatch, and unknown when the available values do not settle the check. It does not insert defaults or change the object. Local and imported interface names work here, including trial is timing-tools.Timing.

Records and interfaces explains how interface defaults declare kinds and how period = unknown number explicitly permits an unknown numeric field. The corresponding timing = unknown Timing supplies and permits unknown while checking supplied objects against the named interface.

5.7 Operators and conditions🔗ℹ

Arithmetic uses +, -, *, and / on exact numbers. Division by zero is an error. A quotient without a finite decimal form is displayed as a fraction. Comparisons use ==, !=, <, <=, >, >=, and in; ordered comparisons need numbers. Combine comparisons with and or or rather than chaining them. = supplies values in assignments, record entries, and interface defaults; it is not an equality comparison. For example, ready = count == 20 assigns the result of a comparison to ready.

5.7.1 Equality and unknown🔗ℹ

== and != compare unknown as an explicit value:

Expression

Result

unknown == unknown

true

unknown == 2

false

2 != unknown

true

[unknown] == [unknown]

true

The same rule applies inside records and lists. Use the ordinary equality operators to choose a branch:

Compact

period = unknown

if period == unknown [

  print "Period not yet known"

] else [

  print period

]

Verbose

set period to unknown

if period equals unknown then [

  print "Period not yet known"

] else [

  print period

]

The comparison tests the explicit marker. An absent field or a value the checker cannot establish is not automatically an explicit unknown. Arithmetic, ordered comparisons, and kind tests using an unknown value can still return unknown. The runnable example uses equality in conditional interface requirements.

5.7.2 Boolean operators and precedence🔗ℹ

not, and, or, and xor operate on Boolean values. false and unknown is false; true or unknown is true. Other uses of unknown in a condition can remain undetermined. An undetermined or nonboolean condition stops execution with an explicit error.

From strongest to weakest, the reader groups operations in this order:

Order

Operations

1

Parentheses

2

.field, .[ ... ], and u [ ... ]

3

unary -

4

* /

5

+ -

6

== != < <= > >= in is

7

not

8

and

9

xor

10

or

Operations at the same level associate left to right. Use parentheses when the intended grouping is not immediately clear. For example, do add 1 + 2 (3 * 4) as total passes two expressions to add. Every explicit parenthesis group remains in the parsed workflow, including nested or redundant groups. Checks and evaluation respect those groups. For example, ((1 == 1)) == true is valid and evaluates to true.