Types



Jarog has few built-in atomic types like int or bool Also it supports definition of user-specific custom types, which can be arrays, structs or functions. All types are global and also can be imported with modules.

Built-in types

Jarog has next built-in simple or atomic types.

Name of type Description
bool Contains boolean values, true or false
int Integer positive and negative numbers
float Float values with arbitrary precision
string Strings
any Contains values of any types

Types can be compatible, some type is compatible to another when the value of this type can be assigned to value of another type. Type compatibility isn’t bidirectional, for example integer type is compatible with float one i.e. you can assign int to float but cannot assign float to int.

Examples of definition different variables of simple types:

var b1: bool := true;
var b2: bool := 12 > 5;
var i1: int := 8;
var f1: float := 3.14;
var s1: string := "Hello World!";

Long arithmetics

Jarog uses MPArith library developed by W. Ehrhardt. This allows to process long integer and floats with arbitrary precision. There are no special types to work with long arithmetic. Instead of a standard int and float types can work with long numbers.

Type any

Type any is a special type in Jarog. It is compatible with any type so the variable with type any can contain any value. But there are no types that are compatible with type any, so values with that type can be assigned or used only after explicit cast. Also type any can be used in structural types. For example list of any preserves all list features like list length or item access.

User-defined types

User can define his own types. These types can be simple as well as structural. Example:

type myInt: int;  # myInt actually is an alias of int type
type intList: list(int);  # list of integers

Types can be nested, i.e you can define structural types directly inside other types without defining any transitional types.

type listOfStructs: list(struct(name: string, age: int)) 

Description of all structural types is going next.

Structural types

List type

Jarog has a list type, that represents array of items of the same type. Lists are dynamically allocated and there is no need to specify it’s length. List can be defined in next way:

type myList: list(<list item type>);

Lists can contain items of any type but only one at once. Exception is list(any) type. It can contain items of different types in one list. Type of list items must be defined explicitly.

Examples:

type intList: list(int);
type stringList: list(string);

Struct type

Structures are tuples of elements with fixed length, order and type. Unlike lists structs can contain items of different types. Structs can be defined with struct keyword;

Definition of struct:

type myStryct: struct(
    <prop1>: <type1>,
    <prop2>: <type2>
);

Struct also can contain another structs but it can not contain itself i.e.В recursive structures are not allowed. Example of nested struct:

type user: struct(
    name: string;
    birthday: struct(
        year: int;
        month: int(1, 12);
        day: int(1, 31);
    )
)

Struct types are compatible if their field types are compatible respectively. See [structs] for more information

Set type

Set is a collection of unique elements of some atomic type except bool. Set cannot contain lists, structs, functions or another sets. Also it cannot contain bool values as bool has only two possible values. So sets can contain int, float or string elements.

Sets are defined with set keyword. Example:

type intSet: set(int);

Function type

Function type represents function interface, i.e.В parameters and return type. Function can accept multiple parameters and return one result.

Definition:

type myFunc: func(<arg1>: <type1>, <arg2>: <type2> -> <return_type>);

If there is a need to return more than one result, multiple results can be combined into a struct and decomposed later during result assign.

Examples:

# accepts two integers and return one float    
type func1: func(a: int, b: int -> float); 

# has no arguments and returns struct
type func2: funct(-> struct(foo: int, bar: float));   

# returns no results and accepts two parameters
type proc1: func(number: int, text: string); 

# has neither arguments nor results
type proc2: func(); 

Type compatibility

Compatible types means that values of these types can be assigned each other. As it was mentioned above, if type A is compatible with type B it doesn’t means type B is compatible with A. This works with structural types as well. For example list of ints is compatible with list of floats but not vice versa. Jarog uses duck-typing so structural types are compatible if their content is compatible so the same type can be defined in multiple places and this still can be counted as a single type even if these types are defined in different units. This works even for structures with different field names.

Example:

type student: struct(name: string, age: int);
type book: struct(title: string; year: int);

var s: student;
var b: book;

s := b;  # b.title will be assigned to s.name and b.year to s.age

Type casting

Expression type can be explicitly casted to another type if makes any sense. Operator ! is used for type cast. Types are not checked during cast so this operation can trow an exception during runtime.

<expression>!<type>

Any compatible types can be casted to each other. Example:

var s: student := {"John", 23};    # type student is described above
io::put((s!book).title);           # John will be printed 

var i: int := 2 * 3!float;         # will fail, 3 is float now

In some cases non compatible types also can be casted. Structs and lists are not compatible but struct itself is nothing more that just a list of values, so struct can be casted to list(any) or even to list(int) if all struct fields are integers for example. Simple types like int, string, bool or float will throw an exception when tried to cast to some other type except casting int to float.

type user: struct(name: string, email: string, address: string);
var s: user := {"John", "j@mail.com", "Grapetown, Street 123"};
...
s!list(any);                      # this will work
s!list(string);                   # this will work too

5!bool                            # returns true

Type any extends compatibility term. As the list(any) can contain different types in single arrays it makes possible to cast any structure to a list of any. In the same way list(any) can be casted to struct, but only when it contains items in proper order that can be casted to structure types.

var a: any;
var l: list(any);
...
a := s;                           # a can contain any type
l := a!list(any)                  # l will now contain ["John", 23]
io::put(l[1]!string)              # John will be printed 

Besides type casting Jarog also supports type testing. Operation ? checks can expression value be casted to specified type.

<expression>?<type>

If expression can be casted true is returned

var s: any := {"John", 23};
...
log s?struct(name: string, age: int);     # true will be returned
log s?struct(name: string, price: float); # still true
log s?list(any);                          # true
log s?struct(age: int, name: string);     # false