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