Skip to content

Structs

ItsDeltin edited this page Sep 14, 2023 · 8 revisions

Structs are value types that can encapsulate data.

struct Dictionary<K, V>
{
    public K[] Keys;
    public V[] Values;

    public V Get(in K key)
    {
        return Values[Keys.IndexOf(key)];
    }
}

Struct values are created by defining every properties' value.

Dictionary<Button, String> bindings = {
    Keys: [Button.Interact, Button.Ultimate],
    Values: ["Press Interact to place an orb.", "Press Ultimate to detonate orbs."]
};

Struct types cannot be explicitly declared inline, but they can be inferred.

define point = { x: 5, y: 6 };

Recursive references

Unlike classes, structs cannot have recursive references because it would create an infinite loop.

struct A
{
    // illegal
    public A one;

    // illegal, allowed in programming languages such as c#
    // but not here since workshop/ostw arrays are value types rather than reference types.
    public A[] two;

    // illegal, 'B' has a value of the 'A' type.
    public B three;

    // legal, since 'C' is a reference type (class) it can have a value of type 'A' and not create an infinite loop.
    public C four;
}

// A struct with an A
struct B
{
    public A a;
}

// A class with an A
class C
{
    public A a;
}

Having a struct within that same struct using anonymous types is allowed. Because the nesting is explicitly created, this will not cause an infinite loop.

struct A<T>
{
    public T value;
}

A<A<Number>> doubleA = { value: { value: 5 } };

Single structs

'Paralleled' structs (the default) have little difference to single (also known as "unparalleled") structs in the analysis portion of OSTW. The only difference is that unparalleled structs can be assigned to the 'Any' type. However, it affects how OSTW compiles your structs.

See the next section for more information on the differences in how they compile. As a rule of thumb, only use unparalleled structs when one or more of these conditions apply to your situation:

  • Running out of variable space: Unparalleled structs use less variables compared to parallel structs.
  • Read-only struct array: Modifying unparalleled struct arrays is expensive, try to avoid it.
  • Sorting/Filtering struct array: Unparalleled struct arrays are cheaper to sort/filter than parallel struct arrays.

How structs compile to the workshop

You can use this section to make an educated decision on which kind of struct is best for faster workshop code.

There are 2 types of structs: 'paralleled' and 'single'. Paralleled structs assigns a workshop variable for each value in the struct. Singles stores each value in the struct into an array.

In this section, the comments under a line of OSTW code formatted as // > represents how that line of OSTW will compile to the workshop.

struct ParallelStruct
{
    public Number X;
    public Number Y;
}

single struct SingleStruct
{
    public Number X;
    public Number Y;
}

// Paralleled: This code will generate 2 workshop variables: 'myStructVariable_X' and 'myStructVariable_Y'
ParallelStruct paralleled = { X: 3, Y: 4 };
// > global.paralleled_X = 3;
// > global.paralleled_Y = 4;

// Single: Alternatively, this will generate a single workshop variable.
SingleStruct unparalleled = { X: 3, Y: 4 };
// > global.unparalleled = [3, 4];

The difference between parallel and unparalleled structs have the most impact when working with struct arrays.

Assigning to struct arrays

In the workshop, arrays and especially multidimensional arrays are rather expensive. Paralleled structs have the benefit over unparalleled in that it avoids using arrays. This means that a 1-dimensional paralleled struct array keeps everything 1-dimensional in the final compiled workshop output. In addition, since this is a data-type where everything is kept 1-dimensional, changing values is super cheap.

ParallelStruct[] myStructArray = [{X: 3, Y: 4}, {X: 16, Y: 200}];
// > global.myStructArray_X = Array(3, 16);
// > global.myStructArray_Y = Array(4, 200);

myStructArray[1].Y = 300;
// > global.myStructArray[1] = 300;

Compare this to unparalleled structs, where the array builder is required creating a bulkier output:

SingleStruct[] myStructArray = [{X: 3, Y: 4}, {X: 16, Y: 200}];
// > global.myStructArray = Array(Array(3, 4), Array(16, 200));

myStructArray[1].Y = 300;
// > global._arrayConstructor = global.myStructArray[1];
// > global._arrayConstructor[1] = 300;
// > global.myStructArray[1] = global._arrayConstructor;

Mapping struct arrays

Other than this very specific (but very common) case of using Map to select a value in a struct, there is little difference in mapping parallel and unparalleled struct arrays.

If you need to select, for example, every Y in a paralleled TestStruct array, it is super cheap to do so since the workshop output is already formatted for these scenarios. OSTW will optimize the output to not use the super slow Mapped Array workshop value at all. On the other hand, unparalleled requires the actual map.

Number[] arrayOfY = myStructArray.Map(element => element.Y);

// Paralleled output: The actual map is optimized out.
// > global.arrayOfY = global.myStructArray_Y;

// Unparalleled output: much more expensive.
// > global.arrayOfY = Mapped Array(global.myStructArray, Current Array Element[1]); // [0] is X, [1] is Y
// Another example: get the index of the first 'X' in 'myStructArray' that equals 6. myStructArray is paralleled.

Number result = myStructArray.Map(element => element.X).IndexOf(6);
// > global.result = Index Of(myStructArray_X, 6);

Filtering and sorting struct arrays

If you are going to be doing a lot of either filtering or sorting with struct arrays, make them unparalleled. They are very expensive operations to do with parallel structs.

This is because parallel structs are multiple, separate values. It is only possible for the workshop's Filtered Array and Sorted Array values to handle this by converting the paralleled struct array into an array of indices using Mapped Array.

Clone this wiki locally