Simple types

A simple type is just the name of a class. If a variable has a simple type (or a class type), it declares that the variable can hold an instance of that class, or an instance of any subclass of that class.

Variables with types

The following example declares a few variables with different simple types:

var s as Str
var i = 1 as Int
s = 'foo'
i += 1
s += 1    -- Type error

The as keyword followed by a type declares the type of a variable. If the variable has an initializer (as i has above on line 2), the type declaration comes after the initialization expression. On the final line of the example we try to add 1 to a string value. The type checker rejects the program due to this nonsensical line.

The nil value

Additionally, a variable of any type can hold the nil value:

var s as Str    -- Initialize by default to nil
Print(s)        -- nil
s = nil         -- Ok

Function type signatures

We already had an example of a statically-typed function in the previous section. I have copied the type signature of the function here for convenience:

def Triangle(n as Int, ch as Str)

The Triangle function did not return a value (or to be precise, it always returned implicitly nil). If we try to use the return value in a statically-typed context (I will explain this later in detail; now it means that I assign the return value to a variable with a declared type), the type checker reports an error:

var i as Int
i = Triangle(2, '*')    -- Error

The return value type of a function is declared again using as, after the function arguments:

def Highlight(message as Str) as Str
  return '*** ' + message + ' ***'
end

Calling the function may now result in a type error, but is otherwise identical to the dynamically-typed case:

var s as Str
s = Highlight('Hello')
var i = Highlight('Howdy') as Int  -- Type error

You can also ignore the return value, even though it is not very useful in the case of Highlight:

Highlight('x')

Dynamically-typed functions and the void type

If you don't declare any types in the function signature, the type checker considers that function to be dynamically-typed. This means that the type checker simply skips the function body, and never reports an error. It also does no type inference within the function body. The reason for this behaviour is actually related to type inference, but we postpone the explanation until later. However, this poses the dilemma of dealing with functions that take no arguments and do not return a value. Is the following function statically-typed or dynamically-typed?

def Bark()
  Print('Bark!')
end

For this example it does not really make a difference, but for more complex functions it often would. The Alore checker treats any function similar to the above as dynamically typed. To make it statically-typed, we need to give it a void return type, which signifies that the function does not return a value and is statically typed:

def Bark() as void
  Print('Bark!')
end

Statically-typed classes

The examples so far haven't had any class definitions. Defining statically-typed classes is straightforward:

import time

class Person
  var name as Str
  var birthDate as Date

  def show() as void
    WriteLn('Name: ', self.name)
    WriteLn('Date of birth: ', self.birthDate)
  end
end

Now you can define variables with type Person, and use the variable to access variables and methods defined in Person. The type checker can verify that the members are used correctly:

var p = Person('John Smith', Date('1982-12-03')) as Person
p.show()                          -- Ok
p.birthDate = Date(1982, 12, 4)   -- Ok
p.name = 2             -- Error: name must be a string
p.show(True)           -- Error: show does not take any arguments

Symbolic constants

Symbolic constants have always type std::Constant. You can also give the type explicitly. The following two sets of definitions are equivalent:

const Red, Green, Blue     -- Implicit type Constant

const Red as Constant
const Green as Gonstant
const Blue as Constant