# Polymorphic Structs and Functions

Polymorphism is used to define functions and `struct`s that require at compile-time known types `T` via `\$T`.

The dollar-sign before the type-parameter `\$T` indicates that this type has to be derived by the compiler and therefore all required type information has to be known at compile time.

### Polymorphic type declarations

There are (at least) three major ways of defining polymorphic types. As we already saw, we can define the type of a variable via e.g. `x : int;`. Here, the compiler knows the type at compiletime, since it’s explicitely written out.

If we accept any (compile-time) type, we can use the dollar sign, e.g.

``````x : \$T
``````

In this case, any type is matched to `T`, that includes any `struct`, `int`, `bool`, `Any`, etc. There is no restriction on the possible types used. On the one hand, this enables us to write truly generic functions, on the other hand, we expect the programmers to know, what they’re using `x` for - and in case they don’t, the compiler will complain or the program will crash.

We can be more precise than that. Assuming we have polymorphic structs (see below), e.g. `Foo(T: Type)`, we can restrict the type of `x` by only allowing `Foo`s:

``````x : Foo(\$T)
``````

In this case, the inner type `T` still could be anything, but at least we know that `x` is some kind of `Foo`. This declaration can be nested, e.g.

``````x : Foo(Bar(\$T, Sth(\$C, \$D)), \$U)
``````

Sometimes, however, this way of restricting the type of `x` is too strict. It is possible to require members of `x` similar to traits or interfaces in other languages via the `/` notation:

``````x : \$T/Foo
``````

Here, we know that `x` has the fields of `Foo` and we can treat it as such. This does not mean `x` is a `Foo`! It can simply incorporate a `Foo` via e.g. `using f: Foo;`. This enables also component bases systems where each struct via `using _c: SomeComponent;`

In all of these cases, it is possible to re-use the polymorphic types, e.g. `T`, once the compiler could figure what they were. Examples of that are further below.

There are also other ways of defining the types, e.g. `\$T/interface Foo` that will be explained further below.

### Functions

Let’s take a look at a simple function:

``````foo :: (x: \$T) {
print("%\n", x);
}
``````

At this point, we don’t know the type of `x`, but we name it `T` and it has to be known at compile time! We can use it like this:

``````x := 42; // T == int
y := "hello"; // T == string

foo(x); // knows it's an int
foo(y); // knows it's a string
``````

Of course, functions can have multiple polymorphic variables

``````foo :: (a: \$A, b: \$B, c: \$C) {...}
``````

You can use the same type for multiple parameters and return values, just use the identifier for the type.

``````foo :: (a: \$T, b: T, c: T) -> T {...}
``````

Polymorphic return parameter:
We already know, that we can reuse the polymorphic type definition for the return type of a function, see the example above. However, in some cases, the return parameter depends on the argument parameter types

``````foo :: (a: \$A, b: \$B) -> [some function determining the type depending on A and B] {...}
``````

One way of doing this is `#modify` which will be explained further down below. Unfortunately, this method is cumbersome since it does not work directly with the types `A` and `B`, but the AST representation of the parameters.

Another way of achieving this functionality, is with helper-structs: We can define

``````Helper :: struct(A: Type, B: Type) {
T :: #run helper(A,B);
}
helper :: (\$A: Type, \$B: Type) -> Type {
T : Type = A; // do your logic here
return T;
}
``````

The `helper` function actually does the logic and returns the wanted return type depending on the input types. It is run at compile-time via the `#run` instruction. To use the return type, we now define our original function `foo` as

``````foo :: (a: \$A, b: \$B) -> Helper(A,B).T {...}
``````

The `Helper` struct can be used anywhere to define the type of a variable, e.g. in `foo`

``````x : Helper(A,B).T;
``````

### Structs

Similar to function, polymorphic structs are also possible! In this case, you need to introduce the polymorphic type `\$T` after the `struct` keyword for compile-time constants:

``````Foo :: struct(x: \$T) {...}
``````

This way, the type of `x` has to be known at compile time:

``````a : Foo(42); // ok, 42 is a constant
x := 2;
// b : Foo(x); // not ok, x is not a constant value!
y :: 2;
b : Foo(y); // ok, y is a constant value
``````

If you want to define a struct that has a polymorphic type for it’s members, you can use the type `Type` to define it during compile-time

``````Foo :: struct(T: Type) {
some_data : T;
}
``````

When using this struct, you have to declare the type

``````f : Foo(int);
``````

Continuing the example above, you can access the type parameter of `Foo` through `Foo.T`.

``````f: Foo(int);
print("type = %\n", f.T); // prints out "type = int"
``````

The polymorphic struct do not only restrict to data types, but they can also extend to functions:

``````Foo :: struct(
// everything here has to be known at compile time
// these entries will be baked out and do not remain part of the struct in memory during run-time!

T: Type,
fun: (T) -> T
// ...
) {
// everything here can be chaged
// (as long as it's not a constant via :: ) at run-time
// and stays in memory

value: T;
// ...
}
``````

Further, it is possible to define recursive types, e.g.

``````Foo :: struct(
T: Type,
fun: (T) -> int
){}

Bar :: struct(_T: Type) {
using f: Foo(Bar(_T), bar_fun)
}
bar_fun :: (b: Bar(\$T)) -> int {
return 42;
}
``````

Do not attempt to print the type: (`beta 0.0.083`)

``````T :: Bar(int);   // the compiler resolves the type just fine
print("%\n", T); // this will go into an infinite loop at run-time
``````

### Arrays

It is possible to use polymorphic arrays int both size `N` and element-type `T`, e.g.

``````foo :: (x: [\$N]\$T) {
print("%: %, %\n", type_of(N), N, T);
}
``````

Here, both `N` and `T` have to be known at compile time. `N` refers to the number of elements and is itself of type `int`! Using the above definition of `foo`, we’d get in this example

``````x := int.[1,2,3,4];
foo(x); // prints "s64: 4, s64"
``````

It is important to know, that arrays of different sizes are different types! So

``````[4]int != [5]int
``````

### Type Comparison

We can compare types with the equals operator `==`:

``````x : float64 = 0.1;
assert(type_of(x) == float64);

n := 42;
assert(type_of(n) == int);

Foo :: struct {}
f : Foo;
assert(type_of(f) == Foo);
``````

However, when using polymorphic structs, e.g.

``````Bar :: struct(T: Type) {
value: T;
}
``````

we can only compare specializations of said type, in this case

``````b : Bar(int);
assert(type_of(b) == Bar(int));
assert(b.T == int);
``````

It is not possible to compare without specialization:

``````b : Bar(int);
assert(type_of(b) == Bar); // this does not work!
``````

even though

``````print("%\n", type_of(b)); // prints "Bar".
``````

### `\$T/Object` syntax

`\$T/Object` indicates that the `\$T` must be a parameterized struct of the type `Object`. This saves time so that one does not have to type out all the parameters of a parameterized struct. Consider the following example:

``````Hash_Table :: struct (K: Type, V: Type, N: int) {
keys: [N] K;
values: [N] V;
}

function1 :: (table: Hash_Table(\$K, \$V, \$N), key: K, value: V) {
// do stuff
}

function2 :: (table: \$T/Hash_Table, key: T.K, value: T.V) {
// do stuff
}

function3 :: (table: \$T, key: T.K, value: T.V) {
// do stuff
}

``````

All the following ways are correct ways to write functions with parameterized structs. `function1` is slightly more verbose and utilizes pattern matching to specify the type, while `function2` is less verbose but still specifies that the `\$T` must be a Hash_Table. `function3` is the most generic, least verbose, but loses a lot of useful type information. Use whatever way fits ones own programming style.

### Implicit Polymorphism

This is another way of writing `\$T/Object`, called implicit polymorphism. In this example, `Table` is being called directly.

``````function4 :: (table: Table, key: table.K, value: table.V) {
// do stuff
}
``````

### `\$T/interface Object` syntax

`\$T/interface Object` indicates that the `\$T` must have the fields that `Object` has. The changelog has this to say:

New experimental feature: keyword ‘interface’ for use with polymorphism. Accepts only types that
contain members declared in the target struct. Basically, this does duck typing in a straightforward
simple way. See uses of the keyword ‘interface’ in modules/Math/module.jai and modules/Math/matrix.jai.

### `#modify` directive

The `#modify` compiler directive lets one put a block of code that is executed at compile-time each time a call to that procedure is resolved. The `#modify` directive allows one to inspect parameter types at compile-time.

Here is an example for how to use `#modify`.

``````function :: (x: \$T)
#modify {
using Type_Info_Tag;
if T.type != INTEGER {
print("\$T must be an integer!\n");
T = null;
}
}
{  // rest of the function

}
``````

In the example above, setting `T = null` tells the compiler that `\$T` must be an integer type or it will be a compile-time error.