Blocks
- Machine blocks are created in the Add machine block window.
- Constants are created in the Add constant window.
- Terminals are created in the Specify terminal block window.
- Input blocks are created in the Add input block window.
- New blocks will be placed wherever in the board the crosshairs are currently located.
- The crosshairs can be moved by left-clicking an empty section of the board.
- If the scrollbar of the board is moved, the crosshairs will be moved to the upper left corner of the visible area of the board.
Connector nodes
- Connector nodes are the blue squares joined by blue lines that connect the output of a block with the input of another block. All block outputs have an output node attached to them (large, solidly colored). Subnodes (smaller, solidly colored), are children either of an output node or of another subnode. Input nodes (large, hollow) are connected to an input of a block and are children either of an output node or of a subnode. If a subnode is dragged to an input of the same type (or of a compatible generic type) as the output to which its ancestor output node is attached, it will get attached to the input and become an input node. Each input can have only one input node attached to it.
- To create a new subnode, place the cursor over the desired parent node, then press and drag the right mouse button. A new subnode will appear, and as long as you don't release the right mouse button, you can move the new subnode with the cursor. Please note that input nodes can't have children, only output nodes and subnodes.
- To move an already existing subnode, place the cursor over it, then press and drag the left mouse button. As long as you don't release the left mouse button, you can move the subnode with the cursor. Output and input nodes can't be moved directly. They move automatically when the block that they are attached to is moved.
- If you hold the space bar while moving a subnode or an input node, the node will get either horizontally or vertically aligned with its parent, depending on the position of the cursor relative to the parent.
- If a subnode or an output node has only one child, you can align it horizontally or vertically with its child by holding Shift while moving the node.
- If you right-click a subnode or an input node and select Split in the popup menu, a new subnode will appear between the node and its parent.
- Subnodes and input nodes can be deleted by right-clicking them and selecting Delete in the popup menu that appears. If you delete a node with children, its parent will become the parent of its children. Output nodes can't be directly deleted.
- If you left-click the type label of an output, all the descendants of its output node will be deleted.
- If you delete a block, its output nodes and all their descendants will be deleted.
- If you press and hold Ctrl a green circle will surround the node that is closest to the cursor, and it will disappear as soon as you release Ctrl. As long as the circle is visible, pressing the left or right mouse buttons will have the same effect as if the cursor was above the node.
Block slectors
Groups of blocks can be moved all at once with a block selector. Block selectors are also the only way to copy or cut blocks. Pressing the right mouse button when the cursor is in an empty part of a board and dragging the cursor without releasing the mouse button will create a block selector. As soon as you release the mouse button, the selector will be fixed with one corner where the cursor was when you originally pressed the right mouse button, and the opposite corner will be where the cursor was when you released the button.
As long as a selector exists, the blocks that were inside of it (or more specifically, whose upper left corner was inside of it) when it was fixed will be associated with it. You can drag the selector and its associated blocks by pressing and holding the left mouse button when the cursor is above one of the corners of the selector.
Right-clicking a corner of the selector will open a popup menu that will allow you to delete all the blocks associated to the selector, or to copy or cut the selector and the blocks associated to it. Right-click an empty section of a board to open a popup menu that will give the option to paste a copied selector.
Keyboard shortcuts
Shortcuts that open windows:
- Ctrl+L: opens the Add machine block window in the Elemental machines tab.
- Ctrl+B: opens the Add machine block window in the Imported machines tab.
- Ctrl+I: opens the Add machine block window in the Internal machines tab.
- Ctrl+F: opens the Add machine block window in the Favourite machines tab.
- Ctrl+K: opens the Add constant window.
- Ctrl+H opens the Add input block window.
- Ctrl+R opens the Specify terminal block window.
- Ctrl+M creates a new comment wherever the crosshairs are located.
- Ctrl+E opens the Edit project window.
- Ctrl+N opens the New internal machine window.
- Ctrl+O opens the Open internal machine window.
- Ctrl+T opens the Edit internal machine window
- Ctrl+D opens the Delete internal machine window.
- Crtl+S saves the current project.
- Ctrl+P compiles the current project.
- Ctrl+U opens the parent board of the currently open board unless the root board of the internal machine is already open.
- Ctrl+W opens the root board of the current internal machine.
- Ctrl+A opens the board tree of the current internal machine.
Pressing Enter is equivalent to clicking on the window's main button in the following windows:
- Add constant
- Add input block
- Specify terminal block
- Open internal machine
- Delete internal machine
Pressing Ctrl+Enter is equivalent to clicking on the window's main button in the following windows:
- Add machine block
- New project
- Edit project
- New internal machine
- Edit internal machine
Machine search bar
All 4 tabs of the Add machine block window have a search bar that you can use to find a machine quickly. Just begin typing the name of the machine (in the case of imported machines, don't include the author and library) and the first machine whose name starts with what you typed will be automatically selected. If you press enter, the next machine whose name begins with what you typed will be selected, and so on, until you cycle back to the first machine. If no machine begins with what you typed, the search bar will turn red. Please note that if you want to search for an elemental machine, the Elemental machine tab must be selected, if you want to search for an imported machine, the Imported machines tab must be selected, etc.
Rank-1 modifiers
Rank modifiers allow you to apply a function to multiple values at once. In most languages, you use loops to do this, each cycle reading one value from the input lists, applying the function to those values, and storing the result in an output list. In ExMachina, you can use rank modifiers to apply a function to each value of an array without using loops:
The integer sum block has the rank-1 modifier (^1), which indicates that the base function will be applied to each pair of elements in the input arrays, and the results will be collected and returned as an array. At their most basic form, rank-1 modified functions work like this:
To modify the rank of a function, use the + and - buttons in the lower right part of the Add machine block window:
Submodifiers
There are three submodifiers that can be used to alter the way rank-modified functions work. Two work on inputs (Extend and Fold) and one works on outputs (Reduce/Refer). Please note that at least one input should not have any submodifiers, so Extend and Fold can only be used in functions with more than one input.
To add a submodifier to an input or output, use the combo boxes that appear in the right part of the Add machine block window after modifying the rank of the function:
Extend submodifier
If you want to use the same value as one of the inputs in all iterations, you can use the Extend modifier on that input:
Fold submodifier
If you want to apply a function recursively to the elements of an array (e.g. add all the numbers in a num array), you use the Fold submodifier. The following image shows how Fold works:
Reduce/Refer submodifier
If you are only interested in the last value of an iterative process, you can use the Reduce/Refer submodifier. The following image shows how Reduce works:
It is important to note that no final value will be available if the output array is empty. The default action in this case will be to throw a ReductionFailureException. However, you can use Refer to make the function return one of the inputs instead (as long as said input has the appropriate rank and type).
Example: factorial
The following is a very concise implementation of the factorial function in ExMachina:
The sequence block returns the array [1,2,...,N] where N is the number that we want to calculate the factorial of. The xI block is rank-1 modified, has a Fold in the first input, and has a Reduce in the output with a Refer to the first input. The first input receives a 1. This causes the block to multiply the first value in the array by one, then multiply the result by the next value of the array, and so on, and then return only the result of the final multiplication.
It is important to note that when N=0, the array returned by the sequence block will be empty, so the reduction at the output of xI will fail and the 1 that is received by the first input will be returned instead. This is the correct result. It is no coincidence that this works, and might provide some insight into why 0!=1. The following image shows the Add machine block window with the correct rank modifier and submodifiers for the xI block:
Rank-2 or higher modifiers
The following image shows how rank-K modifiers where K>1 work:
Here is another image with more detail for the specific case of rank 2:
The following image shows the Add machine block with the rank-2 modifier:
The submodifiers have to be specified for each rank 1,2,...,K. It is important to note that changing the submodifiers in a rank will reset all submodifiers in higher ranks to their default state, so make sure to specify all submodifiers in rank 1 before specifying the submodifiers in rank 2, and so on.
Example: matrix multiplication
The following image shows an implementation of integer matrix multiplication AxB:
First, matrix B is transposed, then the values of both matrices are multiplied with a rank-3 modified xI block with an extend submodifier in the first input in rank 2, and an extend submodifier in the second input in rank 3. The result will be a rank 3 array with the products of all values in matrix A times all values in matrix B. All the values of each rank 1 array in this array are then added with a rank-3 modified +I block with a fold in the first input in rank 1, an extend in the first input in rank 2, and an extend in the first input in rank 3. The result is a rank 2 array with the product of A times B. The Add machine block windows with the appropriate submodifiers for the xI block and the +I block are shown in the following images:
Out
Description:
This terminal indicates that a machine is done processing data and whatever is received by the Out block will be the output that the machine returns. The types of the inputs of an Out block will exactly match the types of the outputs of the machine.
Ilustration:
Break
Description:
This terminal indicates that a machine is done processing data and whatever is received by the Break block will be the output that the machine returns. Also cuts the process before than expected.
Ilustration:
Loop
Description:
This terminal indicates tail recursion i.e. the machine will “restart” using whatever input the Loop block received as the machine's new input. The types of the inputs of a Loop block will exactly match the types of the inputs of the machine.
Ilustration:
Throw
Description:
This terminal throws an exception of a type that has to be specified when placing the block on the board. The only input of a Throw block is a string that will be used as the message of the exception that will be thrown.
Ilustration:
Rethrow
Description:
Throws an exception of the same type as the caught exception that it receives as its first input, and the string that it receives as its second input will be the exception's message.
Ilustration:
RethrowAsIs
Description:
Throws an exception of the same type and with the same message as the caught exception that it receives as its input.
Ilustration:
IfSwitch
Description:
When an IfSwitch block is placed on a board, two new boards are created: an if case board and an else case board.
The first input of an IfSwitch block is of boolean type and labeled switch, and an arbitrary number of additional inputs of arbitrary type can be specified when the block is placed at the board.
During runtime, if the boolean received at the switch input is true, then the data flow will proceed to the if case board. Otherwise, it will proceed to the else case board. The values received at the additional inputs of the IfSwitch block can be accessed from the if case and else case boards (or from any of their descendant boards) with an Input block.
Ilustration:
StringSwitch
Description:
When a StringSwitch block is placed on a board, an arbitrary number of cases must be declared. Each of these cases must be either a string (e.g. “foo”) or a list of two or more strings (e.g. “world”, “hello”). Any given string can only appear in one of the cases of a StringSwitch block. There is also a default case called the else case.
The first input of a StringSwitch block is of str type and labeled switch, and an arbitrary number of additional inputs of arbitrary type can be specified when the block is placed at the board.
During runtime, if the string received at the switch input matches one of the cases, then the data flow will proceed to the corresponding case board. Otherwise, it will proceed to the else case board. The values received at the additional inputs of the IntSwitch block can be accessed from the if case and else case boards (or from any of their descendant boards) with an Input block.
Ilustration:
IntSwitch
Description:
When an IntSwitch block is placed on a board, an arbitrary number of cases must be declared. Each of these cases must be either an integer (e.g. 1) or a list of two or more integers (e.g. 5,3). Any given integer can only appear in one of the cases of an IntSwitch block. There is also a default case called the else case.
The first input of an IntSwitch block is of int type and labeled switch, and an arbitrary number of additional inputs of arbitrary type can be specified when the block is placed at the board.
During runtime, if the integer received at the switch input matches one of the cases, then the data flow will proceed to the corresponding case board. Otherwise, it will proceed to the else case board. The values received at the additional inputs of the IntSwitch block can be accessed from the if case and else case boards (or from any of their descendant boards) with an Input block.
Ilustration:
Catch
Description:
When a Catch block is placed on a board, an arbitrary number of cases must be declared. Each of these cases must be either an exception type (e.g. “IndexOutOfBounds”) or a list of two or more exception types (e.g. “IndexOutOfBounds”, “InvalidInput”). Any given exception type can only appear in one of the cases of a Catch block. There is also a default case called the else case.
The Catch block has an arbitrary number of inputs of arbitrary type that can be specified when the block is placed at the board. During runtime, if an exception is thrown by any block in the board where the Catch block is placed, the exception will be caught if its type matches one of the cases of the Catch block. If an exception is caught, the program will proceed to the matching case's board.
The inputs accessible from that board will be the same as the ones accessible from the board where the Catch block was placed, plus the caught exception. If no exception is thrown, the program will proceed to the else case's board. The inputs of the Catch block will be accessible from the else case's board. If an exception that does not match any case is thrown, it will not be caught.
Ilustration:
CatchAll
Description
When a CatchAll block is placed on a board, two new boards are created: a caught case board and an else case board. The CatchAll block has an arbitrary number of inputs of arbitrary type that can be specified when the block is placed at the board.
During runtime, if an exception is thrown by any block in the board where the Catch block is placed, and the exception will be caught, the program will proceed to the caught case's board.
The inputs accessible from that board will be the same as the ones accessible from the board where the Catch block was placed, plus the caught exception. If no exception is thrown, the program will proceed to the else case's board. The inputs of the Catch block will be accessible from the else case's board.
Ilustration
Scalars
Represents a singular piece of data, contrasting with an array.
- Integers: Whole numbers (no decimals) like 1, 2, or -10.
- Floating-point Numbers: Numbers with decimals, like 3.14 or -5.2.
- Strings: Sequences of characters enclosed in quotation marks, like 'Hello, world!'
- Booleans: Represent true or false values (logical data).
Arrays
Arrays are ordered collections of elements of the same data type. Elements are accessed using an index, which starts at 0 (first element) and goes up to the array size minus 1 (last element).
The range of an array is determined by its number of dimensions; each dimension represents a direction or axis. A one-dimensional array, also named a rank one array, consists of scalar elements. Moving up, a two-dimensional array, also named a rank two array, forms an array of rank one arrays, and so on.
To create an array, list the values within square brackets, separated by commas. To create an empty array add the type of data you want inside the square brackets.
- Boolean: [bln].
- Integer: [int].
- Floating-point: [num].
- String: [str].
To generate an array with a range of size n, include n-1 asterisks before specifying the data type. For example, [*str] produces a two-dimensional array of strings.
Machine References
A reference allows you to refer to an existing internal machine you've declared. Once referenced, the 'evaluate' elemental machine can execute it. To create a reference, write the internal machine name in the 'Add constant' window. ExMachina will automatically handle the reference creation, identifying input and output data types.
Primary Machine
The main machine shares the project's name, while each internal machine has its unique name.
Internal Machines
Users have the flexibility to create numerous internal machines, each with its unique name. It's necessary to define the input and output for each machine, specifying both a name and data type. External sources cannot access internal machines within the project.
Boards
Each machine is divided into boards, each of which contains exactly one terminal. The machines are organized in a tree structure. When a switch is added as a terminal, new branches are created, with each branch representing a board associated with a case of the switch.
Order of Execution
ExMachina operates on lazy evaluation principles, whereby expressions or functions remain unevaluated until needed. Consequently, evaluation only occurs when specific operations require results rather than immediate execution. In the presence of multiple boards (switches), the program initiates evaluation from the terminal of the initial board, navigating through true branches and backtracking as necessary. Once evaluated, it determines the subsequent course of action, repeating the process accordingly.
actor
What's an actor?
The actor model in computer science is a mathematical model of concurrent computation that treats an actor as the basic building block of concurrent computation. In response to a message it receives, an actor can: make local decisions, create more actors, send more messages, and determine how to respond to the next message received. Actors may modify their private state, but can only affect each other indirectly through messaging (removing the need for lock-based synchronization).
Package description:
The package contains machines that are useful for working with actors and messages.
Machines:
createChildFromMachine
Description:
Creates a child actor with a given machine as it's main machine.
Properties:
| Inputs | Outputs |
---|
Name | eventId, machineAuthor, machineGroup, machineName | event |
Rank | { 0, 0, 0, 0 } | { 1 } |
Class | String, String, String, String | *msg |
Ilustration:
deleteTS
Description:
Creates a message that removes the token associated with the given key from the TS.
Properties:
| Inputs | Outputs |
---|
Name | key | message |
Rank | { 0 } | { 1 } |
Class | String | *msg |
Ilustration:
getActorId
Description:
Retrieves the id of the current actor from the context token.
Properties:
| Inputs | Outputs |
---|
Name | context | id |
Rank | { 0 } | { 0 } |
Class | Cx | String |
Ilustration:
getEventId
Description:
Retrieves the id of the currently processed event from the context token.
Properties:
| Inputs | Outputs |
---|
Name | context | id |
Rank | { 0 } | { 0 } |
Class | Cx | String |
Ilustration:
getEventPayload
Description:
Retrieves the payload of the currently processed event from the context token.
Properties:
| Inputs | Outputs |
---|
Name | context, prototype | payload |
Rank | { 0, 1 } | { 0 } |
Class | Cx, *T%%PayloadClass | T%%PayloadClass |
Ilustration:
getEventSource
Description:
Retrieves the source of the currently processed event from the context token.
Properties:
| Inputs | Outputs |
---|
Name | context | source |
Rank | { 0 } | { 0 } |
Class | Cx | String |
Ilustration:
getEventType
Description:
Retrieves the type of the currently processed event from the context token.
Properties:
| Inputs | Outputs |
---|
Name | context | type |
Rank | { 0 } | { 0 } |
Class | Cx | String |
Ilustration:
getTimeStamp
Description:
Returns the date, in number of milliseconds since Jan 01 1970. (UTC), at the start of the current event evaluation cycle.
Properties:
| Inputs | Outputs |
---|
Name | context | timeStamp |
Rank | { 0 } | { 0 } |
Class | Cx | Integer |
Ilustration:
print
Description:
Creates a message that writes the string representation of a token into the console.
Properties:
| Inputs | Outputs |
---|
Name | token | adrevent |
Rank | { 0 } | { 1 } |
Class | T%%Class | *msg |
Ilustration:
println
Description:
Creates a message that writes the string representation of a token into the console in a new line.
Properties:
| Inputs | Outputs |
---|
Name | token | adrevent |
Rank | { 0 } | { 1 } |
Class | T%%Class | *msg |
Ilustration:
rdTS
Description:
Tries to retrieve a token from the temporary storage. The token is expected to be of the same class as the prototype and its degree should be one degree less than the prototype.
Properties:
| Inputs | Outputs |
---|
Name | key, context, prototype | value |
Rank | { 0, 0, 1 } | { 0 } |
Class | String, Cx, *T%%Class | T%%Class |
Ilustration:
removeChild
Description:
Removes a child actor.
Properties:
| Inputs | Outputs |
---|
Name | eventId, childIf | event |
Rank | { 0, 0 } | { 1 } |
Class | String, String | *msg |
Ilustration:
removeTimer
Description:
Creates a message that removes the timer with the given id.
Properties:
| Inputs | Outputs |
---|
Name | eventId, timerId | tsEvent |
Rank | { 0, 0 } | { 1 } |
Class | String, String | *msg |
Ilustration:
setPeriodicTimer
Description:
Creates an Event that sets a new timer that, after the initial delay has elapsed, activates repeatedly, with the given period, until it is stopped. Throws InvalidInputValue if the period is not positive.
Properties:
| Inputs | Outputs |
---|
Name | eventId, timerId, period, intialDelay | tsEvent |
Rank | { 0, 0, 0 } | { 1 } |
Class | String, String, Integer, Integer | *msg |
Ilustration:
setTimer
Description:
Creates an Event that sets a new timer that activates once the specified time has elapsed and then is automatically removed. Throws InvalidInputValue if time is not positive.
Properties:
| Inputs | Outputs |
---|
Name | eventId, timerId, delay | tsEvent |
Rank | { 0, 0, 0 } | { 1 } |
Class | String, String, Integer | *msg |
Ilustration:
stop
Description:
Creates a message that terminates the current execution instance.
Properties:
| Inputs | Outputs |
---|
Name | | tsEvent |
Rank | { } | { 1 } |
Class | | *msg |
Ilustration:
toTs
Description:
Creates an Event that stores a token at the TS under a given key.
Properties:
| Inputs | Outputs |
---|
Name | key, data | tsEvent |
Rank | { 0, 0 } | { 1 } |
Class | String, T%%DataType | *msg |
Ilustration: