The dynamic type and mixed typing
Alore programs may freely mix static and dynamic typing. You can do this in a few different ways:
- You can give a variable, an argument or a function return value the type dynamic.
- If you omit type declarations, a variable or function defaults to dynamic typing. This is more or less equivalent to the above but often more convenient.
Note that local variables are different. If the enclosing function is dynamically-typed, the type checker ignores them (defaulting to dynamic typing). If the enclosing function has a type signature, the type checker infers the types of local variables.
The type dynamic
If a variable has type dynamic, it is dynamically typed and no operations on it are checked statically. Consider this example:
var d as dynamic d = 'x' d = 1 d + 'y' -- Runtime error
Since d has type dynamic, the type checker does not give any errors. However, the program generates an exception at runtime.
Implicit dynamic types
Global variables, member variables and functions are given dynamic types if they have no other types.
The following global variable definitions are equivalent:
var Foo var Foo as dynamic
The following method signatures are equivalent:
def foo(a, b) ... end def foo(a as dynamic, b as dynamic) as dynamic ... end
However, as the second method has explicit types (even though they are all dynamic), the body will use type inference. In the first method foo, the type checker will just ignore the body.
Type compatibility
Every type is compatible with the type dynamic, and vice versa. This allows you to smoothly integrate dynamically-typed and statically-typed code. Consider this example, which defines a dynamically-typed and a statically-typed function that call each other:
def D(n) if n == 0 return 0 else return S(n - 1) + 1 end end def S(n as Int) as Int if n == 0 return 1 else return D(n - 1) * 2 end end
Note that S calls D with argument of type Int, while D expects dynamic; and vice versa. Neither needs any casts.
Assignment works in a similar way:
var n = 0 as Int var d = 1 as dynamic d = n -- Ok n = d -- Ok
Running programs with mixed typing
When you run a program, the interpreter first erases all type declarations. The interpreter thus ignores static types at runtime. This happens behind the scenes and is not directly visible.
Here are a few examples of how erasing types works (we use ==> to mean erasure):
var a = 5 as Int ==> var a = 5 def next(i as Int) as Int return i + 1 end ==> def next(i) return i + 1 end var a = [] as <Int> ==> var a = []
Note that erasure does not remove normal casts, but it removes dynamic casts. For example:
var o = 1 as Object var n = (o as Int) -- Normal cast var d = (o as dynamic) -- Dynamic cast ==> var o = 1 var n = (o as Int) var d = (o)
Runtime type errors and mixed typing
Due to type erasue, some apparent type errors do not result in type errors at runtime:
var i as Int var d = 'x' i = d -- Ok
The example assign a Str object to an Int variable. There is no static error since d has type dynamic; there is no runtime error since the type Int of i is erased before running the program.
A future Alore version will likely allow catching these kinds of type error. It will probably have two execution modes: a loose mode is equivalent to type erasure, while a strict mode detects cases like the example above.
Generic types and dynamic typing
Mixed typing also supports objects of generic types. You can create a generic instance such as Array in dynamically-typed code and pass it to statically-typed code, and vice versa:
var d = [1, 3] as dynamic Sum(d) -- Ok def Sum(a as Array<Int>) as Int var sum = 0 for n in a sum += a end return sum end
You can also use dynamic as a type argument. In this case, dynamic is compatible with any type. For example, Array<dynamic> is compatible with Array<Str>.
Dynamic casts
You can use dynamic as the target type of a cast. This operation does nothing at the runtime, and it only selectively removes type checking from an expression. This allows you to bypass some restrictions of the type system:
var ao = [1] as Array<Object> var ai as Array<Int> ai = ao -- Type check error ai = (ao as dynamic) -- Ok (but be careful!)
Inheritance and mixed typing
A dynamically-typed class may inherit a statically-typed class, and override some of its methods:
class S def next(n as Int) as Int return n + 1 end end class D is S def next(n) -- Ok return n + 2 end end
This is useful especially in dynamically-typed programs that inherit statically-typed library classes. A dynamically-typed program or module does not need to care whether modules that it uses use static typing or not.
A statically-typed class can also inherit a dynamically-typed class, but this is less commonly useful. You can even change individual argument types to dynamic when overriding, while keeping others statically typed:
class S2 def f(i as Int, s as Str) ... end end class D2 is S2 def f(i as Int, s as dynamic) -- Ok ... end end