Language

Arturo is a very simple language. Even without any prior experience, I estimate it would take you roughly half an hour before you are comfortable enough to write your first program.

Here, you'll find everything you may need to know about the language (and perhaps a bit more). In a single page.



Introduction

Arturo's syntax is probably as easy as it could get. Basically, you could say: there is no syntax.

Let's try to resume some key points of Arturo's no-syntax:

  • Code is just a list of words, labels, symbols, values and attributes (you'll learn what all this is about very soon!)
  • Code can be grouped into blocks (that is: a list of words, labels, symbols, and values within square brackets: [ ... ]
  • A block has no meaning until it is given one, or interpreted within some specific context, that is: [ lorem ipsum ] is perfectly valid Arturo code, until you try to "run" it - where the interpreter will complain that it has no idea what lorem and ipsum is. Pretty much like if I tell you a word in Swahili - you'll most likely need a dictionary to figure out what it might mean.
  • There are no reserved "keywords". Every word can be re-defined.
  • You can format your code any way you want: no semicolons to add, no obligatory newlines, no commas, no parentheses, no Python-style indentation rules
  • Every function expects a pre-determined number of arguments. Initially, we check during evaluation, and during runtime, we "consume" values until we reach the number of arguments required.

As you can see, there is not much to learn from scratch here:

Once you learn what the language building blocks are & a few details about precedence and evaluation, then only with the Library Reference at hand (the built-in functions that are already there for you, available to use), you're pretty much ready to write any program. ;-)

So, let's get to the gist of the matter!

The basics

Words

Words in Arturo are pretty much like words in English: a series of characters separated by spaces or some symbol. E.g.: this is a series of words (in this case, this, is, a, series, of, words are all - well... - words.

💡 In Arturo, a word can be start with any letter or underscore followed by any letter/numbers/underscore or a question mark (?).

As with a real (spoken) language, every word has a specific meaning. And if you don't know the meaning of a word, you'll have to look it up in the dictionary. That's pretty much the way things work in Arturo as well.

In Arturo, a word may have 3 main different uses:

  • refer to a value (that is: a variable, e.g. x + 2)
  • refer to an action, which does something (that is: a function, e.g. doSomething) - Arturo comes with hundreds already defined in its standard library
  • refer to nothing (that is: a word without meaning, as in the [lorem ipsum] example above)

Labels

A label is nothing but Arturo's way of assigning meaning (to be read as a value) to a word - what you would normally call variable assignment or variable initialization. (In Arturo, these two terms can be used invariably, since there is practically no difference: you can set and re-define a word/variable as many times as you wish).

So, let's say you want to give a new meaning to the word x:

x: 2

That was it: from now on, x will mean 2 (until and if it's changed). So if you follow the above statement with:

print x

...Arturo will print 2 for you.

Symbols

As the mere word says, symbols are used to symbolize something, mainly as an alias to an existing word - although, by convention, as infix operators.

Hence, let's take the function add. This takes two parameters, adds them up, and returns the result.

So, you may write:

print add 2 3

and Arturo will print out 5 for you.

Now, if you don't want to use the add function (and prefix notation, which is the standard for all function calls), there is a symbol-alias for that: +

So, you could just as well write:

print 2 + 3

Only, this time you're expressing it more like you would in a normal math expression: with infix notation.

Here is the complete list of symbols recognized in Arturo, along with their predefined usage - if any:

SymbolUsage
<=alias of dup (prefix)
=>syntactic sugar
<=>alias of between? (infix)
<<=-
=>>-
<<=>>-
<--
->syntactic sugar
<->-
<<--
->>-
<<->>-
-<-
>--
>-<-
-<<-
>>--
>>-<<-
<<alias of read (prefix)
>>alias of write (infix)
<<<-
>>>-
<---
-->alias of convert (infix)
<-->-
<==-
==>-
<==>-
<~-
~>-
<~>-
|>-
<|-
<|>-
=<alias of lessOrEqual? (infix)
>=alias of greaterOrEqual? (infix)
<>alias of notEqual? (infix)
<:-
-:-
>:-
~alias of render (prefix)
!syntactic sugar
!!-
?alias of switch (infix)
??alias of coalesce (infix)
@alias of array (prefix)
#alias of dictionary (prefix)
##-
###-
####-
#####-
######-
$alias of function (prefix)
%alias of mod (infix)
^alias of pow (infix)
&-
*alias of mul (infix)
**-
-alias of sub (infix)
--alias of remove (infix)
=alias of equal? (infix)
==-
=~-
+alias of add (infix)
++alias of append (infix)
<alias of less? (infix)
>alias of greater? (infix)
/alias of div (infix)
/%alias of divmod (infix)
//alias of fdiv (infix)
\-
\\-
|syntactic sugar
|--
|=-
..alias of range (infix)
...-
./alias of relative (prefix)
:alias of let (infix)
::syntactic sugar
:=alias of conforms? (infix)
||-
alias of null
alias of infinite
alias of sum (prefix)
alias of product (prefix)
alias of intersection (infix)
alias of union (infix)
-
-
alias of subset? (infix)
alias of superset? (infix)
alias of in? (infix)
-
alias of and? (infix)
alias of or? (infix)
alias of xor? (infix)
alias of nand? (infix)
¬alias of not? (prefix)

Attributes

Another interesting feature of Arturo is what we'll analyze here: attributes.

Technically, attributes are nothing but an easy way of defining optional named parameters for functions - but which can however transcend between different function calls.

There are two types:

a. attributes
b. attribute labels

:attribute

Attributes are actually optional on/off-type of values, set during a function call, that is there to denote some variation of the initial meaning of the function. To define an attribute, we'll be using a .(dot) followed by a normal word: \.\w+

Let's say we used the function split, to split a string into parts:

split "hello world"

; => [`h` `e` `l` `l` `o` ` ` `w` `o` `r` `l` `d` ]

That does what it says: splits the string into an array of :chars.

What if we want for example to split the string into words? For that, there is the .words attribute for the function split. So:

split.words "hello world"

; = ["hello" "world"]

💡 The order in which you pass the different attributes does not matter. Also, there is no issue at all whether you want to use spaces between them and the surrounding function call; Arturo will still be able to parse them and recognize them fine

:attributeLabel

Attribute labels are pretty much like simple attributes, only they can also take a value. As it worked with attributes, we'll be using a .(dot) followed by a normal word, but now also followed by a :(colon) -- exactly like with normal labels, as we've seen above.

Here's an example:

split .every: 2 "hello world"

; => ["he" "ll" "ow" "or" "ld"]

More Values

Values are the very core of Arturo and are used to refer to pretty much all the different kinds of data you can have.

💡 Words, labels, and symbols - that we already saw above - can be considered "values" as well; and treated as such, unless we proceed to evaluate them!

We could split values into 2 categories: a) literal values - that is values you can directly define in your code and b) constructible - values that are created using some function.

The (hopefully) complete list follows.

:null

Null values generally denote nothing and it's mostly used as a return value by functions to inform us that something went wrong. If you want to set it as a value, you may just use the word null, like:

x: null

:logical

Logicals are Arturo's logical values. They are like normal boolean values (true, false), with a twist: they fully support ternary logic and an additional maybe value.

x: true
y: false
z: maybe

:integer

Integers represent positive or negative integers. When they are declared they are comprised only by digits ([0-9]+) and they can be as long as you want - Arturo does support big numbers.

x: 2
y: 876528347613438247982374913423423947329

:floating

Floating values are basically floating-point numbers, that is: decimals. They begin with an initial all-digit part, followed by a . (dot) and another all-digit part: [0-9]+\.[0-9]+

pi: 3.14159

:complex

Even though there is no special syntax for defining a complex number, you can always create one using to. Arturo comes with full support for complex numbers and all the associated operations.

a: to :complex [1 2]      ; a: 1.0+2.0i
b: to :complex @[pi pi]   ; b: 3.141592653589793+3.141592653589793i

print a + b 
; 4.141592653589793+5.141592653589793i

:rational

The syntax for rational literals is simply a number followed by a colon, followed by a number (pretty much like you would normally represent a ratio).

a: 1:2                   ; 1/2
b: 3:4                   ; 3/4

As with complex numbers, rationals can also be defined using to. And again, Arturo comes with full support for rational numbers and all the associated operations.

a: to :rational [1 2]     ; 1/2
b: to :rational [3 4]     ; 3/4

print a + b 
; 5/4

:version

Version values are nothing but a fancy and portable way of defining SemVer-compliant versions (e.g. for packaging an application). The rule is rather simple: it has three numeric parts, separated by a . (dot) and an optional part with build or prerelease information.

package: 1.0.3

if package > 1.0.0 -> print "yep, the version is right!"

or

version: 1.2.3-rc1+build123

:type

Type is a value representing another... type. To specify a type value, the format is a : (colon) followed by a word - the type's name: :\w+

myType: :integer

or

if (type x) = :integer [ print "it's an integer!" ]

💡 If you want to define your own custom types (think "classes" in OOP languages), we have define for that: e.g.

define :person [
   init: method [name :string][
       \name: name 
   ]
]
p: to :person ["John"]!
print p\name ; John

:char

Characters in Arturo can be declared using single quotes: '\w'

ch: 'a'

:string

A string is nothing but a series of characters, seen as one unit. In Arturo, to define a string, there are various ways:

Single-line strings

  • using double quotes: x: "this is a string" (with escaped characters)
  • using right smart-quote notation x: « This is a string (in this case, everything following « till the end of the line, will be stripped and considered one string)

Multi-line strings

  • using curly-brace blocks (the result will be stripped and un-indented):

    x: {
        this is yet
        another
        very
        long string
        that spans more
        than
        one
        line
    }
    
  • using verbatim curly-brace blocks (the result will remain exactly as-is):

    x: {:
      this is yet
        another
        very
      long string
      that spans more
          than
              one
              line
    :}
    
  • using dash notation (where everything after the line, until the end of the file, is a string - stripped and un-indented):

    x: 
    ------
    this is the last very
    long string
    that spans more
    than
    one
    line
    

💡 If you want your string to contain sub-expressions that will be evaluated on the fly - that is string interpolation - all you have to do is include your code inside the string within pipe-bars and then call the function render (or ~) to process it accordingly: e.g.

x: 2
print ~"my variable is: |x|"

; prints: 
; my variable is: 2

:range

Arturo comes with full support of lazy ranges.

To create a range all you have to do is use the built-in range function (or its .. infix equivalent):

numbers: 1..10    ; contains all numbers from 1 to 10 (inclusive)
downwards: 10..1  ; this is valid as well

chars: 'a'..'z'   ; yes, we can actually have character ranges too

Ranges can be used in most functions as-is, e.g. Iterators, where we can take full advantage of their "lazy" nature: they are not evaluated until needed.

However, you can still evaluate a lazy range into its corresponding block any time you want:

@1..10 ; [1,2,3,4,5,6,7,8,9,10]

:regex

A regex value is nothing but a string containing a regular expression (that is then compiled and treated as such).

The normal syntax is {/regex/}:

type {/[A-Z]/}                    ; => :regex

replace "HelLo" {/[A-Z]/} "X"     ; here we replace all capital letters
                                  ; with an X

While we may - optionally - use any of the supported PCRE-compliant flags:

  • i: case-insensitive matching
  • m: multiline
  • s: dot-all
match "Hello" {/he/i}             ; => ["He"]

:path

Paths in Arturo are a way of defining some hierarchy between values, something along the lines of parent -> child -> grandchild. For this, in Arturo, we'd use a series of values or words delimited with a \ (backslash). You can think of them as indexes in other programming languages.

print user\name

or

x: "name"
print user\[x]

or

myArray: ["zero" "one" "two" "three"]
print myArray\1

; prints "one"

:pathLabel

If paths are the way of defining some hierarchy between values, with pathLabels we are also able to assign values to some specific path.

user: #[
    name: "John"
    surname: "Doe"
]
print user\name  ; will print "John"

; let's change this user's name
user\name: "Jane"

; we can also do it like that
x: "name"
user\[x]: "Jane"

print user\name ; ok, now it should print "Jane"

💡 If you we are inside a custom type, an easy way to refer to fields of the same object is using this. However, Arturo comes with a shortcut: e.g.

define :person [
   init: constructor [name :string]
   sayHello: method [][
       print ["Hello" \name] ; this is equivalent to `this\name`
   ]
]

which generally helps us write more better-looking code. :)

:literal

Literals in Arturo are just a way of referring to the name of a word or symbol. Think of it as the string version of a word, or like Ruby's symbols.

For example, the function info takes as an argument the name of the function for which you want some information. If you wrote like info print, the interpreter would execute the function print and try to... print something (which would not be there). If you wanted to refer to the name of the function -- that is: without actually calling it -- you would precede it with a ' (single-quote): '[\w+]

func: 'print
info func

However, literals may be used for much more - e.g. modifying a passed parameter in-place, without having to re-assign the result of an operation to a new variable. To learn more, have a look at In-place variable modification.

:symbolLiteral

Symbol literals are to symbols pretty much what literals are to words. That is: the "literal", unevaluated form of the symbol.

To declare a symbol literal, we may follow the example of normal, literals: single quote + accompanied by the symbol in question:

type '++        ; => :symbolliteral

:pathLiteral

Now, that we've seen both "normal" literals and paths, I guess you can already imagine what Path Literals are about... :)

And, yes, you guessed right:

type 'this\is\a\path  ; => :pathliteral

:inline

Inlines in Arturo generally denote a list of words/symbols/values that are grouped and given some type of priority in evaluation. An inline block is denoted by (...) (parentheses).

print (2+3)*4

Please note though that, apart from precedence, :inline is a value type on its own:

a: [(one) two three]
print type a\0      ; that is: (one)

; prints :inline

:block

Blocks are a fundamental part of Arturo.

As we've already said, it's just a [...] (square-bracket enclosed) block of words/symbols/values that - in contrast with inline blocks above which are evaluated in-place - are not evaluated until it's necessary.

myBlock: [one two three]
anArray: [1 2 3 4 5]
anotherArray: ["zero" 1 "two" 3 "cuatro"]

As you can see, blocks can contain practically anything: any word, any symbol, and any value. Of course, they can contain other blocks too:

x: [
   1 2 [
       3 4 [
           5 "six" 7
       ] 
       8 
   ] 
   9
]

:dictionary

Dictionaries in Arturo as what in other languages you'd call an associative array or hash table. In Arturo, it's practically the same, only with an added twist: they are ordered hash tables. If you want to create one, just give the dictionary function (or use the # alias) a block, with different labels, and it'll automatically convert it to a dictionary.

user: #[
     name: "John"
     surname: "Doe"
     age: 34
]

What the # function here does is:

  • execute the block
  • retrieve only the words/variables defined in there
  • return a dictionary with the aforementioned words

💡 As you can probably assume from the above definition, a dictionary block doesn't necessarily have to contain just labels and word definitions - it may contain whatever you want, even function calls; only it will return you back just a table with the defined words in there

:store

Now, that you've seen Dictionaries, imagine a dictionary that could actually save its contents to disk for every change you make to it. Or retrieve it from disk when you start your program again, offering you seamless integration with your data. And safety too.

Well, that's exactly what Stores are. :)

d: store "mystore" ; initialize your Store and 
                   ; specify where you want to store it 
                   ; (if the store already exists, it'll automatically load it)

d\name: "John"     ; and you can use it as any normal dictionary

:object

Object values are intimately related to custom types, as we have already seen above.

And while Arturo isn't properly speaking a primarily Object-oriented language, it offers more than enough functionality that will cover most OOP needs.

The normal way to create a new object, is by using to and a new custom type you have defined before:

newClient: to :person ["John" "Doe"]!
; yep, we've just created a new Object value!

Technically, Objects are nothing but supercharged Dictionaries that come with a twist: their functions (see: methods) can have access to its own fields and methods, from within - using this. But other than that, don't let them scare you out: it's just another tool in our toolkit! ;-)

:function

Functions are another important value type in Arturo - and yes, you heard right: functions a value too. You can assign them to a word/variable, pass them around, re-define them and whatever you want with them, pretty much as you would do with another value, let's say a number.

To define a function, all you have to do is call the function... function (or use the $ alias) followed by two parameters:

  • the parameters' names (this can be either a literal, e.g. 'x - if it's just one argument - or a block, e.g. [x y]. If you want to use commas, for readability, like [x,y] you are free to do so: Arturo will simply ignore them.
multiply: function [x y][
     x * y
]

print multiply 2 3

; would print 6

or

addThemUp: $[x,y][x+y]

print addThemUp 2 3

; would print 5

:method

Methods in Arturo are pretty much like functions. With a couple of asterisks:

  • They are meant to be used only inside a custom type (in define) or in a module context
  • We'd use them only when we want to have access to this (= in a few words, when we want to access other fields/functions/methods of the same object; if we don't need that, we could still use a function)

A quick example:

define :car [
    init: method [; our constructor
        brand :string
        speed :quantity
    ][ 
        this\brand: brand
        this\speed: speed
    ]

    distanceTravelled: method [inHours :quantity][
        this\speed * inHours
    ]
]
myCar: to :car ["Volvo" 100`km/h]!

print ["In 2 hours, we'll have travelled:" myCar\distanceTravelled 2`h]

We may also have magic methods, that is: making our custom types accessible-to or "overloading" normal stdlib methods.

For example, if we want to be able to print one of our custom objects, we'd first have to define a way that this custom object is meant to be converted to a string. For that, we'd define a string magic method:

define :car [
    ; as previously

    string: method [][
        return "CAR :: " ++ \name
    ]
]
myCar: to :car ["Volvo C30" 100`km/h]!

print myCar ; this would print: CAR :: Volvo C30

Supported Magic methods:

get: method [what]          ; this\what
set: method [what, value]   ; this\what: value

changing: method []     ; object about to change
changed: method []      ; object changed

compare: method [that]  ; comparator definition (to compare between `this` and a second object)
equal?: method [that]   ; this = that
less?: method [that]    ; this < that
greater?: method [that] ; this > that

add: method [that]      ; this + that
sub: method [that]      ; this - that
mul: method [that]      ; this * that
div: method [that]      ; this / that
fdiv: method [that]     ; this // that
mod: method [that]      ; this % that
pow: method [that]      ; this ^ that

inc: method []          ; inc this
dec: method []          ; dec this

neg: method []          ; neg this

key?: method [key]          ; key? this key
contains?: method [field]   ; contains? this field

append: method [what]   ; this ++ what
remove: method [what]   ; this -- what

string: method []       ; to :string this
integer: method []      ; to :integer this
floating: method []     ; to :floating this
rational: method []     ; to :rational this
complex: method []      ; to :complex this
quantity: method []     ; to :quantity this
logical: method []      ; to :logical this
block: method []        ; to :block this
dictionary: method []   ; to :dictionary this

:unit

Arturo has very advanced capabilities related to physical quantities, measurements and the related operations.

And everything begins with the concept of units.

To declare a Unit value, all you have to do is use a backtick, followed by the unit in question:

Meters: `m 
SquareMeters: `m2
Becquerels: `Bq

The list of builtin, supported Units is quite... long:

m, s, K, g, A, mol, cd, USD, B, rad, sr, in, ft, yd, ftm, rod, mi, fur, nmi, ang, au, ly, px, pt, pc, sqin, sqft, ac, are, ha, barn, L, gal, bbl, qt, p, cup, floz, tbsp, tsp, bu, cord, min, h, day, wk, mo, yr, lb, slug, oz, ct, t, ton, lt, st, Da, gr, dwt, ozt, lbt, mps, kph, mph, kn, fps, mach, Gal, N, dyn, lbf, kgf, pdl, Pa, atm, bar, pz, Ba, mmHg, psi, Torr, J, Wh, cal, BTU, eV, erg, th, thm, W, hp, statA, abA, Bi, V, statV, abV, Ohm, statOhm, abOhm, S, C, statC, abC, Fr, F, Daraf, H, abH, Wb, Mx, T, G, degC, degF, degR, b, KiB, MiB, GiB, TiB, PiB, EiB, deg, grad, arcmin, arcsec, kat, Hz, Bq, Ci, Gy, Sv, R, P, St, rpm, clo, bps, lx, Lb, lm

We also support most world currencies, with their ISO 3-letter code, e.g. USD, EUR, etc.

Compound units:

If you want to define compound units, Arturo actually supports that natively:

squareMeters: `m2
kilometersPerHours: `km/h

newtonDefinition: `kg.m/s2    ; yes, that's actually a N(ewton)

:quantity

Now, as we've seen, Arturo's Unit values are quite powerful. So, how can we express a Quantity using units?

Very simple: just add a number (a Rational literal works too) just in front of a unit:

twoMeters: 2`m    ; yes, we've just expressed the notion of "2 meters"
                  ; as a native Arturo value

And we can obviously use them in operations as every other value:

print 3`m + 4`yd    ; 6.6576 m

And, yes, no need to worry about compatibility: Arturo actually understands dimensional analysis! ;-)

:color

Colors can be easily defined using the #xxxxxx syntax, where xxxxxx is either the RGB value of the color in hex, or its common-name alias, like #red, #green or #blue:

color1: #red
color2: #0077BB

print color1 + color2

:date

Dates in Arturo are a distinct type of value. If you want to create one, you'll have to use one of the corresponding functions. For example, the function now returns a :date object, representing the current point in time. And it can be handled pretty much like you would handle a :dictionary.

print now

; would print 2020-10-26T10:27:14+01:00

print now\year

; would print 2020

:database

Database values cannot be constructed literally. However, using the function open, you can create a database value and then use it to query the database in question and much more.

db: open.sqlite "my.db"

print query db "SELECT * FROM users"

print type db   ; would print: :database

:socket

Another not-literally constructible (but still powerful) value type is the Socket.

To use Socket values properly, just have a look into the Sockets library module.

If you want to create one, just have a look at the quick example below, using listen:

; start a server listening on port 18966
server: listen 18966    ; and, yes, that's a socket!

:binary

Binary values are used to represent binary data, that is: an array of bytes. You cannot define them directly, however, you can sure convert other data to binary.

print to :binary "Hello world!"

; would print 4865 6C6C 6F20 776F 726C 6421

:bytecode

Bytecode values cannot be constructed literally. However, you can create them indirectly, e.g. using the function to.

; let's create some Arturo bytecode from scratch 
bcode: to :bytecode [
    loop 1..10 'x [
        print x
    ]
]

; and execute it!
do bcode          

; we can also inspect it
inspect bcode
; [ :bytecode
;         ================================
;          DATA
;         ================================
;         0: [ :block
;                 print :word
;                 x :word
;         ]
;         1: x :literal
; 
;         ================================
;          CODE
;         ================================
;         push0
;         eol                  #4
;         push1
;         consti10
;         consti1
;         range
;         loop
;         end
; ]

Important concepts

Here we are going to analyze a few aspects that make Arturo rather unique; or that - in any case - you should study a bit more in order to become a real... Arturo guru, in no time.

So, let's get started...

Precedence and Evaluation

The easiest way to explain precedence rules in Arturo is pretty much like it happened with our introduction: there are no precedence rules whatsoever.

So, in an expression like 2 * 3 + 4, if you'd normally expect to get a result of 10, you would be wrong: the result is 14.

But in order to understand why, you'd have to understand how evaluation in Arturo works.

The right-to-left rule

The main expression evaluation order of Arturo is right-to-left. But with a tiny asterisk: Your code will be evaluated from left to right, it is the expressions passed to your function calls that will be evaluated from right-to-left.

Let's take a very simple example:

print add 1 2 
print "Done"

As you would expect, the first function to be executed is the first print function and then the second one. Nothing new here.

Now let's take the first print. How is it working?

Let's see:

  • First, the value 2 is pushed onto the stack
  • Then, we push the value 1
  • Then, we execute the function add: it pops two values from the stack, adds them up, and pushes the result (3) back onto the stack
  • Finally, we execute the function print: it pops the top value from the stack (3) and prints it.

Then, execution would move on and... print "Done."

What you have to understand here is that evaluation within an expression will always be done from right to left, irrespective of what you might know from other languages or math operator precedence. In Arturo, you have to learn no precedence rules at all. You'll just have to remember to always calculate from right to left.

Re-visiting our previous, seemingly paradoxical, example:

2 * 3 + 4

💡 Remember: our + and * operators are nothing but simple infix aliases to the functions add and mul respectively -- nothing more!

This is as if we had written (moving the operators in front):

* 2 + 3 4

which practically means: FIRST add 3 and 4 and THEN take the result and multiply it with 2.

If this is not what intended, then the right way in Arturo would be, either:

4 + 2 * 3

or (giving precedence to the multiplication, artificially, using parentheses):

(2 * 3) + 4

Another example:

Let's say we want to concatenate a number and a string into a new string.

to :string 3 ++ " <-- this is our number"

The above example wouldn't work, because first it tries to concatenate 3 with a string (" <-- this is our number) and then performs to :string. While what we actually want is to first convert our number to a string and then concatenate the two strings.

The correct code in that case would be:

(to :string 3) ++ " <-- this is our number"

Scope and rules

Contrary to what you might expect, Arturo doesn't feature a traditional concept of scope. There are no real local or global variables, no local or global functions, no local or global blocks, no local or global anything.

Generally, if a variable has been previously declared at the moment and location of its usage, then it is available. Otherwise, it is not.

But let's see a couple of concrete cases to make this clearer.

Blocks

Arturo doesn't have a block scope.

In a few words, this means:

  • A variable declared inside a block is available outside of it
  • A variable previously declared outside of a block is available inside
  • The changes of an existing variable, inside a block, persist after the block
x: 1    ; here, we declare a variable x
        ; and set it to 1
do [
    x: 2      ; here, we re-assign the value of x
              ; to 2
    print x   ; prints 2
    y: 3      ; here, we declare a variable y
              ; and set it to 3
]
print x     ; prints 2 (the value of x has been changed)
print y     ; prints 3 (the value of y is still available)

Iterators

Iterators (such as loop, map, etc) always work with a block as well. But in a special way.

Basically, the logic is identical to the one of blocks, but with a slight difference:

  • the injected variables (e.g. the iteration arguments), are available only inside the block, but not after the iteration is over
  • any previous value is "protected" and restored if needed
x: 3
loop.with:'i ["one" "two" "three"] 'x [
  print i  ; prints 0, 1, 2,...
  print x  ; prints "one", "two", "three",...
]
print x    ; prints 3 (the value of x has been restored)
print i    ; ERROR: variable not found 
           ; (i is not available anymore)

Functions

Functions are totally different. Why? Because they do have their own scope.

The general idea is:

  • A variable declared inside a function is available only inside the function
  • A variable previously declared outside of a function is available inside
  • The changes of an existing variable, inside a function, do not persist after the function

If we want to export a specific symbol to the outer scope, that is, make it available outside the function, we can use the .export: attribute.

If we want to export all of the symbols - thus, practically making the function scope-less, we may use the .inline attribute.

In-place variable modification

In Arturo, every time you pass a parameter to a function, you can rest assured that the value of that parameter won't change (unless it's a string, block or dictionary and you - consciously - decided to use set on it, in which case it does alter its inner structure).

So, basically, you when do this...

a: [1 5 2 4 3]
sort a

...all you do is to take an array, sort it, and push the sorted array onto the stack. Variable a simply does not change.

So, what would you do if you wanted to get the array back, but sorted?

The simple - and most obvious - way would be to re-assign the returned result from sort:

a: [1 5 2 4 3]
a: sort a

And now, yes, a does contain the sorted version of the initial array.

But, what if you want to perform the modification in-place, which is normally faster and without the need for intermediate variables? Literals come to the rescue!

Using literals

As we've already said, "literals" ('i 'am 'a 'literal) are nothing but string representations of a word, that is... the word itself. For that reason, they may come in very handy when you want to modify a variable in-place.

Let's revisit the above example and what the syntax of sort is:

sort collection :literal :dictionary :block

As we can see, sort takes one parameter (collection) which is either a :dictionary or :block OR a :literal.

Why pass a literal? Simply because this turns in-place modification on! Let's have a look:

a: [1 5 2 4 3]
sort 'a         ; yep, now a *has* been sorted in-place!

And this is very powerful: in Arturo, most of the built-in functions in the library come with this feature included. Practically, whenever you see a function taking a literal first parameter, that means you can use it for in-place modifying a variable (yes, even add works like that!).

⚠️ Word of caution: Values in Arturo are always passed by reference - unless they are constant/readonly values. So if you want to assign one variable to another and then modify one of them in-place, make sure you use new; otherwise, both values will change!

a: 1
b: a
inc 'a    ; both a and b are now 2

c: 1      
d: new c  ; we copy the value of c into d
inc 'c    ; now c is 2 and d is 1, as expected

Syntactic sugar

As you have hopefully seen so far, Arturo is rather simple, with fairly simple rules and that's pretty much it.

However, we also have some "syntactic sugar": a fancy way of referring to syntactic constructs, so that something more complicated will look better, or easier-to-write, or more readable.

Here you'll learn about some useful examples of syntactic sugar supported in Arturo.

Right-arrow operator: ->

The function of the right operator is rather straightforward: basically, it wraps the following terminal value inside a block.

Let's take a simple example.

x: -> 2

This is 100% equivalent to:

x: [2]

You can also use the right-arrow operator to make many common constructs far more readable.

For example:

if x=2 -> print "x was 2!"

is the same as writing:

if x=2 [ print "x was 2!" ]

As you can see, it can be pretty handy. Just remember that -> can wrap only one terminal value.

For example:

x: -> 2 3

This doesn't result in x: [2 3] but in x: [2] 3

Another interesting way of making use of the power of the right-arrow operator:

loop 1..10 'x -> print x

which is the same as writing (only much more elegant):

loop 1..10 'x [ print x ]

Fat right-arrow operator: =>

The fat right-arrow operator is like a super-charged simple right arrow operator (->) as described above.

If -> was used to wrap the following terminal into a block, the => operator does even more.

Let's take this simple example:

x: $ => add

This is equivalent to writing:

x: $[x,y][add x y]

Basically, it reads the following word, and if it's a function, pushes all its needed arguments as anonymous variables.

The same could work with a block argument, where & can be used as a placeholder for the needed anonymous variables:

x: $ => [add & &]

(The first & will pop the first argument, and the second the next one - and so on...)


As you can already imagine, this is perfect for easily defining functions or action blocks that take exactly one parameter.

For example, to print an array of the even numbers between 1 and 10:

print select 1..10 'x [even? x]

This could be easily written as (using the -> operator):

print select 1..10 'x -> even? x

But pushing the limits more, we can also use the => operator:

print select 1..10 => even?

That's surely much more readable, isn't it?

Double-colon operator: ::

The double-colon operator does something very simple: it wraps everything in a block - until the end of... the current block:

do [
    print :: 
        "This is a number:"
        123
]

Is equivalent to:

do [
    print ["This is a number:" 123]
]

Exclamation-mark operator: !

Explaining the exclamation-mark can be a bit tricky, but if you want the short version: it wraps everything that follows in a do block (more or less).

Or the shorter version:

  • Have you just created a new custom-type object? (e.g. to :myObject [])?
  • Have you imported an external package? (e.g. import 'somePackage)?

Then just put ! at the end of the statement (e.g. import 'somePackage!) and everything will work as you expect.

The long explanation:

Arturo, when parsing a block of code, has to know which symbols correspond to function calls and how many arguments they take. The exact same is valid for object methods.

When we create a new object (e.g. p: to :person ["John"]), if we attempt to use p\someMethod from the exact same block where this p value was initialized, there is no possible way to know beforehand that e.g. someMethod is a method. We'll know that only after to :person ["John"] has been executed. But then, it's already too late. So, how do we make sure that Arturo knows about the object before we attempt to call inner methods? By putting a "stopper" after the object creation, and that stopper is our beloved... !.

💡 Technically we could achieve the same thing without the ! sugar:

p: to :person ["John"]
do [
    p\sayHello
]

That would work too, for all practical purposes. But: ! is looking good. And it's also far more performant, based on the way it's implemented internally! ;-)

Pipe operator: |

⚠️ This is experimental and may still not be stable enough for use in production scripts

The pipe operator is an easy way of reversing the default prefix notation of function calls and simulating what in OOP languages is called function chain.

Let's take this simple example:

1..10 | print

This equivalent to:

print 1..10

Or a bit more elaborate example (using pipes and the ->/=> operators):

1..10 | map => [2 * &]
      | select 'x -> even? x
      | print

which would be like writing:

print select map 1..10 'x [2*x] 'x [even? x] 

Conclusion

If you made it here, then I can assure you: you've already learned more than you need in order to be fully proficient in Arturo.

Just head to the Library Reference and have a look at the built-in functions (with explanations and example code) and see what's already available for you or - if you want to see the language in action - just browse through the Examples: there are many (many!) working examples, to get more than just an idea.

Welcome on board! :)