Basics

Table of contents

  1. Moss as a calculator
  2. Basic data types
    1. Functions
    2. Strings
    3. Lists
    4. Maps
    5. Sets
  3. Control flow
    1. Unconditional loops
    2. Conditional loops
    3. For-loops
    4. Conditional statements
    5. Conditional expressions
    6. Subprograms
  4. Module import
  5. Comments

Moss as a calculator

The first thing one can basically do is to use Moss as a calculator. Just type in some mathematical expression and then press the return-key.

> 1+2
3

There is no invisible times, the multiplication operator (*) has always to be stated explicitly, wich is a good thing. This allows us to have multiple letter identifiers and an invisible function application operator. These concepts will be explained later. The division operator is the slash (/).

Only round brackets can be used to undermine the order of operations. Square brackets and curly brackets have another purpose. The expression "10 to the power of two" is written "10^2".

> (2+4)*(4-2)
12

> 10^2
100

For integer division, use the the operator "//" instead of "/". The remainder of the division is calculated by the modulo operator "%".

> 14/5
2.8

> 14//5
2

> 14%5
4

It is possible to store the value of an expression in a variable. The statement "a=12" means that the value 12 is assigned to the variable a. If a afterwards occurs itself in an expression, it has this value. So the variable contains this value until some other value is assigned to it.

> a=12
> 2*a
24
> a=10*a
> 4*a
480

A variable is represented via an identifier, in this case "a". An identifer may consist of more than one letter. Some or all of the letters can be capital letters, but Omega is different from omega. That means, in Moss identifiers are case sensitive.

We can check whether two numbers are equal or not. The expression "a is equal to b" is written "a==b". The expression "a is not equal to b" is written "a!=b".

> a=360
> a==360, a==240, a!=360, a!=240
[true, false, false, true]

And we can compare two numbers with respect to their order.

> 1<2, 1>2, 1<=2, 1>=2
[true, false, true, false]

To get the minimum and maximum of two numbers, use the functions min(x,y) and max(x,y). The sign of a number is calculated by sgn(x), the absolute value by abs(x).

Calculations with complex numbers are possible:

> (4+2i)*(2+1i)
6+8i

Moss is capable of handling really big numbers.

> 2^10
1024

> 2^1000
1071508607186267320948425049060001810561404811705533607443750388370351
0511249361224931983788156958581275946729175531468251871452856923140435
9845775746985748039345677748242309854210746050623711418779541821530464
7498358194126739876755916554394607706291457119647768654216766042983165
2624386837205668069376

Basic data types

Typename Example data Meaning
Bool false, true logical values
Int 0, 1, 2, -1 integer numbers
Float 0.0, 4.2, 1/3 floating point numbers
Complex 1i, 2+4i, -3.2i complex numbers
String "", "ab" strings of characters
List [], [1,2] lists of elements
Map {}, {a=1, b=2} maps of key-value pairs
Range 1..10, (1..) ranges of elements
Function |x| 2*x functions

Functions

Think of the expressions 4+2*1, 4+2*2, 4+2*3, 4+2*4. We want to abstract the pattern 4+2*x from these expressions. To achive this, we create a function and store it in the variable f.

> f = |x| 4+2*x

This is a function wich takes the value of the variable x as its input and returns the value of 4+2*x as its output. The x is a variable, but it is bound to the function because it appears between the vertical bars. Because of this the variable x is called formal argument of the function.

Now, if a is some number, we can write f(a) to calculate the value of 4+2*a. The number a is called actual argument of the function. This makes us able to compute the values without typing in the same pattern again and again. Think of much longer patterns.

> f(1),f(2),f(3),f(4)
[6, 8, 10, 12]

It is possible to abstract this further.

> [1,2,3,4].map(f)
[6, 8, 10, 12]

> list(1..4).map(f)
[6, 8, 10, 12]

> f[1..4]
[6, 8, 10, 12]

A function may have more than one argument. We will state a function that has two. There is more than one way to achieve this. The first one uses two formal arguments. Formal arguments appear between the vertical bars and are separated there by commata.

> g = |x,y| x*y
> g(3,4)
12

The next way is a technique known as currying.

> g = |x| |y| x*y
> g(3)(4)
12

The next way is to have a list as an argument instead.

> g = |t| t[0]*t[1]
> g([3,4])
12

> g = |[x,y]| x*y
> g([3,4])
12

The next way is to have a map of two named arguments.

> g = |m| m["x"]*m["y"]
> g({x=3,y=4})
12

> g(x=3,y=4)
12

> g = |{x,y}| x*y
> g(x=3,y=4)
12

If a function consists not only of a single expression, but of a sequence of statements, a longer syntax has to be used.

# (1) Short syntax
f = |x| 2*x

# (2) Longer syntax
f = fn|x| 2*x end

# (3) Full syntax
f = fn|x| return 2*x end

# (4) Function with private name "f"
f = fn f|x| return 2*x end

# (5) Equivalent to (4)
function f(x) return 2*x end

A longer function usually contains statements and local variables. Here is an example, an approximation of the natural exponential function:

function exp(x)
   u = x/64
   y = 1 + u + u^2/2 + u^3/6 + u^4/24
   return y^64
end

Strings

In Moss, all kinds of objects can be stored in variables. A string of characters is also an object, and thus can be stored in a variable. The function print writes an object to the output.

> s = "London"
> s
"London"

> print(s)
London

The function str transforms an integer into a string. The function int does the reverse.

> str(360)
"360"

> int("360")
360

Applied to a string, str does not change anything. To int applies the same.

> str("Flower")
"Flower"

> int(12)
12

Two strings can be added, this will glue both strings together. Therefore addition of strings is associative.

> s = "Night"
> s+"fall"
"Nightfall"

The empty string "" is to strings like the number zero is to numbers.

> "Elm tree"+""
"Elm tree"

We can test whether two strings are equal or not.

> s = "Mouse"
> s=="Mouse", s=="Mice", s!="Mouse", s!="Mice"
[true, false, false, true]

The function len takes a string and returns the number of characters.

> len("Bee")
3

> len("")
0

The result of the expression s[i] is the character at index i. Furthermore the result of the expression s[i..j] is the substring s[i]+s[i+1]+...+s[j-1]+s[j].

> s = "Elm tree"
> s[0], s[2], s[-4], s[-1]
["E", "m", "t", "e"]

> s[0..2]
"Elm"

> s[..2]
"Elm"

> s[4..]
"tree"

> s[-2..]
"ee"

> s[4..-3]
"tr"

Note that s[i..-1] is a special case: an empty slice for every index i. You can bypass this behavior by writing s[i..] or s[i..len(s)-1].

There are some escape sequences that provide the notation of arbitrary Unicode characters inside of a string literal.

SequenceMeaning
\nnew line
\sspace
\ttabulator
\bbackslash
\ddouble quote
\qsingle quote
\x{61}Unicode character 0x61

An example:

print("\b\b\b\b\na\s\sb\n\d\q\q\d\n\x{61}\x{62}\x{63}")
# Output:
# \\\\
# a  b
# "''"
# abc

A triple quoted string literal can span over more than one line.

s = """
Multi
line
string
"""

If space is placed directly behind a backslash, this space is overlooked until some non spacing character is found.

print("a\    b")
# output:
# ab

s = """\
Multi\s\
line\s\
string\
"""
print(s)
# output:
# Multi line string

Lists

In Moss, lists are dynamic arrays (not doubly linked lists). This is a fundamental, albeit easy to understand, data structure.

# Construction of a list with elements 4,9,2,6.
> [4,9,2,6]
[4, 9, 2, 6]

# A list can be empty.
> []
[]

# We can assign a list to a variable.
> a = [1,2,3,4]
> a
[1, 2, 3, 4]

# Every element of a list can be of a different type.
# A list can contain lists as elements.
> ["abc",12,[1,2]]
["abc", 12, [1, 2]]

The expression list(a..b) creates a list of integers from a to b.

> list(1..10)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

We can transform a string into a list of characters. The inverse transform is also possible.

> list("abc")
["a", "b", "c"]

> ["a","b","c"].join()
"abc"

A list can be used as a dynamic stack.

> a=[]
> a.push(2)
> a.push(3)
> a
[2, 3]

> a.pop()
3

> a
[2]

Lists can be added. The result is a concatenation.

> [1,2]+[3,4]
[1, 2, 3, 4]

> [1,2]+[]
[1, 2]

Lists are indexed like strings. The function len(a) takes a list and returns the number of its elements.

> a = [4,5,6,7,8]
> a[0], a[1], a[-1], a[-2]
[4, 5, 8, 7]

> a[2..3]
[6, 7]

> len(a)
5

To reverse a list, write a.rev(). This operation reverses the list itself, but also returns the result.

> list(1..4).rev()
[4, 3, 2, 1]

> a = list(1..4)
> a.rev()
[4, 3, 2, 1]
> a
[4, 3, 2, 1]

> a = list(1..4)
> copy(a).rev()
[4, 3, 2, 1]
> a
[1, 2, 3, 4]

To reverse only a part of the list, use a slice assignment:

> a = list(1..10)
> a[0..3] = a[0..3].rev()
> a
[4, 3, 2, 1, 5, 6, 7, 8, 9, 10]

Two elements of a list are swapped as follows:

> a = list(1..10)
> a.swap(0,3)
> a
[4, 2, 3, 1, 5, 6, 7, 8, 9, 10]

Or this way:

> a = list(1..10)
> a[0],a[3] = a[3],a[0]
> a
[4, 2, 3, 1, 5, 6, 7, 8, 9, 10]

It is possible to swap slices:

> a = list(1..10)
> a[0..1],a[2..3] = a[2..3],a[0..1]
> a
[3, 4, 1, 2, 5, 6, 7, 8, 9, 10]

> a = list(1..10)
> a[0..2],a[3..4] = a[2..4],a[0..1]
> a
[3, 4, 5, 1, 2, 6, 7, 8, 9, 10]

> a = list(1..10)
> a[0],a[1..3] = a[3],a[0..2]
> a
[4, 1, 2, 3, 5, 6, 7, 8, 9, 10]

We can rotate the last three elements of a list:

> a = list(1..10)
> a[-1],a[-3],a[-2] = a[-3..]
> a
[1, 2, 3, 4, 5, 6, 7, 9, 10, 8]

Maps

A map (also called dictionary) can be seen as a generalisation of a list. Like in a list an element of a map belongs to an index, called key. But such a key is not restricted to integers.

> m = {}
# empty map

> m = {0: "a", 1: "b", "alpha": "c"}
> m[0]
"a"

> m["alpha"]
"c"

The operation "k in m" is used to test whether the key k is contained in the map m or not.

> 0 in m, "alpha" in m, "beta" in m
[true, true, false]

> 0 not in m, "alpha" not in m, "beta" not in m
[false, false, true]

The function call list(m) returns the list of keys contained in m. The keys can be in any order, because a map is not (explicitly) ordered.

> list(m)
[0, 1, "alpha"]

If you are interested in the values or key-value pairs, simply express this by methods.

> list(m.values())
["a", "b", "c"]

> list(m.items())
[[0, "a"], [1, "b"], ["alpha", "c"]]

If the key is an identifier, there is a shorthand notation. Instead of

{"x": 4, "y": 3}

one may write:

{x=4,y=3}

But note that this is not the same as {x:4,y:3}. Before a colon, x,y are itself variables that should contain keys.

> x="u"; y="v"
> {x:4,y:3} == {u=4,v=3}
true

Sets

If the value of some key is omitted, null is taken as its value. This value, null, is a special object that represents absence of something. A map with all values omitted can be used as a set. All set operations are supported (e.g. union, intersection, subset relation, universal quantifier).

> {1,2} == {1:null, 2:null}
true

> A = {1,2,3}
> B = {2,3,4}
> A|B, A&B, A-B, A$B
[{1, 2, 3, 4}, {2, 3}, {1}, {1, 4}]
# union, intersection, difference, symmetric difference

One can use set(a) to convert an iterable object a into a set. A set is itself iterable. For any iterable object A, things like

are defined.
> set(1..4)
{1, 2, 3, 4}

> set((1..4).map(|x| 2*x))
{2, 4, 6, 8}

> {2,4,6,8}.all(|x| x%2==0)
true

But note that the elements of a set can be in any order. If you wish to pretty print a set, it should be sorted.

> A = set(1..4)
> A.sort()
[1, 2, 3, 4]

Control flow

Unconditional loops

Suppose we want to write a program that asks a character from the user and prints the Unicode-value of this character. And this program should be repeated endlessly. In a loop, if all statements are executed, we jump to the beginning of the loop to execute them again.

while true
   c = input()
   print(ord(c))
end

This program is so short that it can be written as an one-liner.

while true do print(ord(input())) end

Maybe you already know about the nesting problem. That is the question to which opening keyword or bracket a closing "end" or bracket belongs if there are many of them. Have a look at the programming language Lisp and you will understand.

A control flow statement can also be written in a longer form, stating explicitly what shall end. This is useful if the body of the loop is longer than one page.

while true
   c = input()
   print(ord(c))
end of while

The user might input more than one character. We take this into account in the following way.

while true
   a = list(input())
   print(a.map(ord))
end

Conditional loops

A standard task is to print the numbers from one to ten.

i = 1
while i<=10
   print(i)
   i = i+1
end

For arithmetic operators, there is a shorthand syntax:

For-loops

The numbers in a given range are printed better by a for-loop than by a while-loop.

for i in 1..10
   print(i)
end

By step two.

for i in 1..10: 2
   print(i)
end

In reverse.

for i in 10..1: -1
   print(i)
end

A range of characters.

for i in 'a'..'d'
   print(i)
end

A list instead of a range.

for i in [4,2,1,9]
   print(i)
end

It is also possible to iterate over the characters of a string.

for i in "abc"
   print(i)
end
# Output:
# a
# b
# c

Maps are iterable.

for key in {a=1,b=2}
   print(key)
end

for value in {a=1,b=2}.values()
   print(value)
end

for t in {a=1,b=2}.items()
   print(t)
end
# Output:
# ["a", 1]
# ["b", 2]
# or
# ["b", 2]
# ["a", 1]

for key,value in {a=1,b=2}.items()
   print(key,"|",value)
end
# Output:
# a|1
# b|2
# or
# b|2
# a|1

One can iterate over cartesian products.

for i,j in ["a","b"]*[0,1]
   print(i,j)
end
# vs.
for i in ["a","b"]
   for j in [0,1]
      print(i,j)
   end
end
# Output:
# a0
# a1
# b0
# b1

The following program produces the 9×9 multiplication table.

for i in 1..9
   for j in 1..9
      put(str(i*j).rjust(3))
   end
   print()
end
# Output:
#  1  2  3  4  5  6  7  8  9
#  2  4  6  8 10 12 14 16 18
#  3  6  9 12 15 18 21 24 27
#  4  8 12 16 20 24 28 32 36
#  5 10 15 20 25 30 35 40 45
#  6 12 18 24 30 36 42 48 54
#  7 14 21 28 35 42 49 56 63
#  8 16 24 32 40 48 56 64 72
#  9 18 27 36 45 54 63 72 81

A for-loop can include the index for each element. Let i be the index, and x the element.

for i,x in "abc".enum()
   print(i,"|",x)
end
# Output:
# 0|a
# 1|b
# 2|c

a = [1,2,3,4]
for i,x in a.enum()
   for y in a[i+1..]
      print([x,y])
   end
end
# Output:
# [1, 2]
# [1, 3]
# [1, 4]
# [2, 3]
# [2, 4]
# [3, 4]

Conditional statements

In a program we often have to make a branch. Depending on a condition the program has to perform either in one way or in another.

x = int(input("A number: "))
if x%2==0
   print("This number is even.")
else
   print("This number is odd.")
end

Conditional expressions

Conditional expressions are similar to conditional statements.

Let us take x to the power of n as an example. Powers with natural exponents can be defined recursively:

x0 := 1,
xn := x*xn-1 (n>0).

In this case the definition can be implemented without further ado:

pow = |x,n| 1 if n==0 else x*pow(x,n-1)

A more complicated example:

theta = int(input("A temperature: "))
print(
   "freezing" if theta< 1 else 
   "cold"     if theta< 6 else
   "fresh"    if theta<17 else
   "warm"     if theta<24 else
   "hot"
)

# By means of an if-statement
if theta<1
   print("freezing")
elif theta<6
   print("cold")
elif theta<17
   print("fresh")
elif theta<24
   print("warm")
else
   print("hot")
end

Subprograms

As our programs are getting bigger, there is the need of using some piece of functionality multiple times.

function count(s,c)
   k = 0
   for x in s
      if x==c then k = k+1 end
   end
   return k
end

while true
   s = input()
   print("Number of left brackets: ", count(s,"(")+count(s,"["))
   print("Number of right brackets: ", count(s,")")+count(s,"]"))
end

It is complicated to solve such a task without subprograms.

We can give a function a private name. Then the assignment statement has an alternative syntatic form.

# (1) A function without a private name,
# called anonymous.
f = fn|x| 2*x end

# (2) The same function, but with private name "f".
f = fn f|x| 2*x end

# (3) Syntactic sugar for statement (2).
# This is an assignment to the variable f,
# but the assignment operation is invisible.
function f(x) 2*x end

So we could have written count = fn|s,c| or count = fn count|s,c| instead of function count(s,c).

Module import

Additional functionality is provided by importing modules. For example, there is a module called math, that provides the elementary functions.

# ordinary import
use math
f = |x| math.sin(math.pi*x)


# qualified import
use math: pi, sin, cos
f = |x| sin(pi*x)


# alias import
use m = math
f = |x| m.sin(m.pi*x)


# qualified alias import
use math: pi, s = sin, c = cos
f = |x| s(pi*x)


# qualification of everything
# (should be avoided)
use math:*
f = |x| sin(pi*x)


# load and qualify in separate steps
math = load("math")
use(math): pi, sin, cos


# line breaks
use math{
   e, pi,
   exp, ln,
   sin, cos, tan
}


# show what is contained in math
expose = |m| print(list(record(m)).sort().join(", "))
expose(math)

Comments

# Single line comment

/*
Multi
line
comment
*/

/*
s = "Phantom text"
print(s)
#*/

/*01*/ stoi = fn|s| /*string to integer*/
/*02*/   s.reduce(0,|x,c| 10*x+ord(c)-48)
/*03*/ end