Skip to content

Variables and scope

A variable is a name that holds a value. You create one by assigning to it. There is no declaration and no type.

There are two spellings of plain assignment. They mean the same.

set greeting to "Hello"
set other to "Hi"
set third = "Hey"
printline greeting
printline other
printline third
Hello
Hi
Hey

set name to value and set name = value and name = value all work. The target can be a name, a property (file.status = "x"), or an index (list[0] = 1). Anything else is a syntax error: Invalid assignment target.

Assigning replaces whatever the variable held. The old value is discarded.

Arrays and dictionaries are copied on assignment, so two variables never share one. See Values and types.

+=, -=, *= and /= change a variable using its own value. The natural form is a keyword: increment, decrement, multiply and divide, each with an optional by. Without by the amount is 1.

set x to 3
increment x by 2
printline x
increment x
increment x by 10
decrement x by 2
multiply x by 2
divide x by 4
printline x
5
7

name[] = value adds an item to the end of an array. It works on a name, on an index, and on a property: a[] = 1, a[2][] = 1, a.list[] = 1.

set list to []
list[] = "a"
list[] = "b"
printline count(list)
2

An if, while, until or ensure can assign and test in one step. The condition is true when the assigned value is true.

if first = mash.collect([7, 8]).first()
printline "first is {{first}}"
end
first is 7

Several assignments can be joined with and, or or a comma. A comma means and here.

Reading a name that was never set gives null. It is not an error. A misspelt name therefore looks like a variable that is empty.

set total to 5
printline "[{{totl}}]"
[]

A script has one global space, and each call of a block has its own local space. These rules decide which one a name belongs to.

A name is looked up in this order:

  1. the block’s parameters
  2. the block’s own here variables
  3. the parameters and here variables of the blocks that enclose it
  4. the global variables

If the name is none of these, it can be a block with no parameters, which is then called. Otherwise the value is null.

A block can therefore read every global variable.

Assigning to a name writes to the place where the name already lives, using the same order as reading. If the name is nowhere, the assignment creates a global variable.

That is the rule that surprises people. A plain assignment inside a block does not make a local variable. It changes the global one, or creates one that is still there after the block ends.

set total to 0
block add n
increment total by n
set created to "visible outside"
end
add(5)
add(6)
printline total
printline created
11
visible outside

A parameter is local. Changing it does not change the caller’s variable.

block bump p
set p to p + 1
return p
end
set p to 10
printline bump(1)
printline p
2
10

here name = value makes a variable that belongs to the current call of the block. It is not visible to the caller and does not outlive the call. Later plain assignments to the same name inside the block change the local one.

set x to 1
block f
here x = 2
set x to 3
return x
end
printline f()
printline x
3
1
block f
here secret = 1
end
f()
printline "[{{secret}}]"
[]

If a parameter has the same name, here replaces the parameter’s value for that call.

here also works inside a condition: if here found = lookup(name).

Each call gets fresh locals, so a block that calls itself does not disturb its caller’s variables.

A block defined inside another block can read the outer block’s here variables and parameters:

block outer
here a = 1
block inner
return "inner sees a=[{{a}}]"
end
return inner()
end
printline outer()
inner sees a=[1]

At the top level of a script, here makes a variable that blocks can read but is otherwise like a global.

The variable named in each ... as name is an ordinary variable. It is not scoped to the loop. It keeps the last value after the loop ends, and it overwrites a variable of the same name.

set i to "before"
each 1...3 as i
# i is reassigned on each pass
end
printline i
3

A block and a variable can share a name. This is not an error. The variable wins once it has been set, and the bare name calls the block until then.

block name
return "from block"
end
set x to name
printline x
set name to "now a variable"
printline name
from block
now a variable

mash and event always refer to the built-in objects, so a script cannot assign to them. The compiler refuses the script. value is the loop variable that each uses when you give no as name.

The built-in blocks print, printline, send, sendline, sendheader, sendstatus, sleep, uppercase, lowercase and explain can be called by name. Any method of the mash object can also be called without the mash. prefix, so count(list) is the same as mash.count(list).

printline count([1, 2, 3])
printline mash.count([1, 2, 3])
3
3