Skip to content

Variables and Types

Variables and types

Like many languages, Morpho allows the programmer to define variables to contain pieces of information of values. A variable is created using the var keyword, which is followed by the variable name

var a

Variable names must begin with an alphabetical character or the underscore character _, and may consist of any combination of alphanumeric characters or underscores thereafter. Variable names are case sensitive, so

var a
var A

each refer to distinct variables.

After creating a variable, you may immediately store information in it by providing an initializer, which can be any value

var i = 1
var str = "Hello"

Types

Morpho is a dynamically typed language: Every value has a definite type, and Morpho is always able to tell what type it is, but variables may generally contain values of any type and functions or methods can accept arguments of any type.

There are a number of basic types in Morpho:

nil

: is a special value that represents the absence of information and is different from any other value. Unless an initializer is provided, Morpho variables initially contain nil after declaration.

Bool

: values contain either true or false.

Int

: values contain 32 bit signed integer numbers. An integer constant is written intuitively, e.g. 1, 50, 1000 and may include a negative sign -100.

Float

: values contain double precision floating point numbers. You can write numeric constants either using a decimal 1.5 or in scientific notation, e.g. 1e10, 1.6e-19 or 6.625e26.

In addition to these basic types, Morpho provides a rich collection of objects. Discuss immutability i.e. cannot be changed after creation, or not

Strings

Strings are sequences of unicode UTF8 encoded characters. You specify a literal string using double quotes

var h = "Hello"

Literal strings can contain a number of special characters, which are introduced by a backslash character as in the table below

Code Character
\f Form feed
\n Newline
\r Carriage return
\t Tab
\u Unicode character (followed by 4 hex digits)
\U Unicode character (followed by 8 hex digits)
\x Unicode character (followed by 2 hex digits)
\\ Backslash
\" Quote

To create a string incorporating the morpho butterfly emoji, for example, you would use

"\U0001F98B morpho"

Hex digits are not case sensitive.

You may also insert the results of arbitrary expressions (see Chapter Expressions) into a Morpho string using interpolation as in the following example

print "Hello ${name}! Happy ${age} birthday"

The values of the variables name and age are converted to strings if necessary and joined with the surrounding string.

To convert something to a string, use the String constructor

print String(1.5)

Note that Strings in Morpho are immutable. Their contents cannot be changed, and operations on strings always return a new String:

print "ABC" + "DEF"

Lists

Lists are ordered sequences of values. They can be created either through the syntax

var a = [1,2,3]

or using the List constructor

var a = List(1,2,3)

which converts its arguments into a List.

Elements of a list can be accessed using the index notation

print a[0] // Prints the first element

Note that, like all Morpho collection objects, the index 0 represents the first element. You can change the value of any element in the List using analogous notation

a[0] = 10

You can access the last element using -1

print a[-1] // Prints the last element

and, more generally, negative numbers can be used to count from the end of the List.

Stacks

One application of a List is to implement a stack, which is a data structure to which values can be added to or removed from, following a Last In, First Out (LIFO) protocol.

We create the stack using an empty List

var stack = []

and can then implement key stack operations as follows:

  • Push elements onto the stack:

    stack.append(1,2,3)
    
  • Pop the last element off the stack:

    print stack.pop()
    
  • Peek at the last element of the stack without removing it:

    print stack[-1]
    
  • Check if the stack is empty:

    if (stack.count()==0) {
        // Do something
    }
    

We can use these basic operations to implement more complex operations, such as duplicating the last element of the stack

a=stack.pop()
stack.append(a,a)

or swapping the top two elements

a=stack.pop()
b=stack.pop()
stack.append(a,b)

Arrays

Arrays are multidimensional stores, and can be created as part of a variable declaration. Both of these examples create a \(2\times2\) array:

var a[2,2]

or by a constructor

var a = Array(2,2)

Use index notation to set and get individual elements

a[0,0]=1
print a[0,0]

Each array element can contain any content that a regular variable can, so you may store Strings, Lists and even other Arrays in an Array:

a[0,0]="Hello"
a[1,0]=[1,2,3]

All Array elements are initialized to nil by default.

Dictionaries

Dictionaries, also called hashtables, hashmaps or associative arrays, are data structures that map a set of keys to a set of values. This simple example maps a few state codes to their capitals:

var cap = { "MA" : "Boston", "NY" : "Albany", "VT": "Montpellier" }

You could also use the Dictionary constructor

var cap = Dictionary("MA", "Boston", "NY", "Albany", "VT", "Montpellier")

where the arguments are, alternatingly, keys and values.

Access the contents of a Dictionary using index notation

print cap["MA"]

which also allows you to add additional key-value pairs

cap["ME"]="Augusta"

Any value can be used as a key in a Dictionary, but there is a subtlety: the behavior depends on whether the object is immutable. Basic types are immutable, and hence integers work as expected

var a = { 0: "Zero", 1: "One", 2: "Two"}
print a[0]

Lists, however, are

Ranges

Ranges describe a collection of values, denoted using the .. and .... The Range

1..10

describes the collection of integers from 1 to 10 inclusive; the Range

1...10

(note the triple dots) represents the integers from 1 to 9; the upper bound is excluded. You can represent a range with an increment other than 1 like so

1..9:2

which comprises the odd numbers from 1 to 9. Ranges work with floating point numbers, e.g.

0..1:0.1

Ranges are frequently used to define the bounds of loops as will be described in Section X.

Complex numbers

Morpho supports complex numbers, which can be created using the keyword im to denote the imaginary part of a complex number

var z = 1 + 1im

You can use any number format for either the real or imaginary parts

print 0.1im
print 1e-3im

Complex numbers work with arithmetic operators just like regular numbers

print z + 1/z

Get the real and imaginary parts of a complex number using the convenience functions real and imag

print real(z)
print imag(z)

or by calling the corresponding methods

print z.real()
print z.imag()

Find the complex conjugate

print z.conj()

and obtain the magnitude and phase

print z.abs() // Or use the regular abs() function
print z.angle()

Note that the value \(\phi\) returned by the angle method always lies on the interval \(-\pi<\phi\le\pi\); in some applications you will need to track the correct Riemann surface separately and add multiples of \(2\pi\) as appropriate.

Tuples

Tuples, like Lists, represent an ordered sequence of values but are immutable. They are created either using the syntax

var t = (1,2,3)

or using the Tuple constructor

var t = Tuple(1,2,3)

Elements can be accessed using index notation

print t[1] // Prints the second element

but an attempt to change them throws an error

t[0] = 5 // Throws 'ObjImmutable'

Because Tuples are immutable, they can be used as keys in a Dictionary:

var dict = { (0,0): true }
print dict[(0,0)] // expect: true