-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
5 changed files
with
152 additions
and
190 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,58 @@ | ||
## First flow | ||
Below is a schematic diagram of a 'flow'. | ||
# First flow | ||
Without knowing anything about `flow` and its detailed semantics you might be able to guess what this flow | ||
below does when executed and what the output to STDOUT will be. ![First flow](first.svg) | ||
It is a fibonacci series generator. | ||
|
||
Without knowing anything about 'flow' and it's detailed semantics | ||
can you guess what this flow does when executed and what the output to STDOUT will be? | ||
![First flow](first.svg) | ||
## Understanding the flow | ||
NOTE:You can find a complete description of flow semantics in the next | ||
section [Defining Flows](../describing/definition_overview.md) | ||
|
||
The next section reveals the answer and walks you thru it. | ||
### Root flow | ||
All flows start with a root "flow definition". Other sub-flows can be nested under the root, via references to | ||
separate flow description files, to enable encapsulation and flow reuse. | ||
|
||
In this case it is the only one, and no hierarchy of flows descriptions is used or needed. | ||
You can see the TOML root flow definition for this flow in the flowsample crate's fibonacci sample. | ||
[root.toml](../../flowsamples/fibonacci/root.toml) | ||
|
||
### Interaction with the execution environment | ||
The root defines what the interaction with the surrounding execution environment is, | ||
such as [Stdout](../../flowr/src/cli/stdio/stdout.md), or any other `context function` provided by the flow runtime | ||
being used (e.g. `flowr`). | ||
|
||
The only interaction with the execution environment in this example is the use of `stdout` to print the numbers | ||
in the series to the Terminal. | ||
|
||
### Functions | ||
Functions are stateless, and pure, and just take a set of inputs (one on each of its inputs) and produce an output. | ||
|
||
When all the inputs of a function have a value, then the function can run and produce an output, or not | ||
produce outputs, as in the case of the impure `stdout` function. | ||
|
||
This flow uses two functions (shown as orange ovals): | ||
- `stdout` from the `context functions` as described above | ||
- `stdout` only has one, unnamed, default input and no outputs. It will print the value on STDOUT of the process | ||
running the flow runner (`flowr`) that is executing the flow. | ||
- the `add` function from the flow standard library `flowstdlib` to add two integers together. | ||
- `add` has two inputs "i1" and "i2" and produces the sum of them on the only, unnamed, "default" output. | ||
|
||
### Connections | ||
Connections (the solid lines) take the output of a function when it has ran, and send it to the input of connected | ||
functions. They can optionally have a name. | ||
|
||
When a functions has ran, the input values used are made available again at the output. | ||
|
||
In this case the following three connections exist: | ||
- "i2" input value is connected back to the "i1" input. | ||
- the output of "add" (the sum of "i1" and "i2") is connected back to the "i2" inputs. This connection has optionally | ||
been called "sum" | ||
- the output of "add" (the sum of "i1" and "i2") is connected to the default input of "Stdout". This connection has | ||
optionally been called "sum" | ||
- | ||
### Initializations | ||
Inputs of processes (flows or functions) can be initialized with a value "Once" (at startup) or "Always" (each time | ||
it ran) using input initializers (dotted lines) | ||
|
||
In this example two input initializers are used to setup the series calculation | ||
- "Once" initializer with value "1" in the "i2" input of "add" | ||
- "Once" initializer with value "0" in the "i1" input of "add" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,149 +1,97 @@ | ||
## Step-by-Step | ||
Here we walk you through the execution of the previous "my first flow" (the fibonacci series sample). | ||
|
||
Execution is in terms of Functions. Values are in fact a specific implementation of a | ||
Function, as Values have to store values, unlike functions. | ||
Compiled flows consist of only functions, so flow execution consists of executing functions, or more precisely, jobs | ||
formed from a set of inputs, and a reference to the function implementation. | ||
|
||
### Init | ||
The list of Functions is loaded and all are initialized. | ||
This includes making the initial values available (just once) at | ||
the inputs of any values that have initial values specified in the flow description. | ||
The flow manifest (which contains a list of Functions and their output connections) is loaded. | ||
|
||
Status (ready to run, pending inputs, blocked etc) of all functions is set based on availability of their inputs and | ||
not being blocked from sending its output. | ||
Any function input that has an input initializer on it, is initialized with the value provided in the initializer. | ||
|
||
Any function that has either no inputs (only `context funcitons` are allowed to have no inputs, such as `Stdin`) or | ||
has a value on all of its inputs, is set to the ready state. | ||
|
||
### Execution Loop | ||
In general, the execution loop takes the next function that is in the ready state | ||
(has all its input values available, and is not blocked from sending its output by other functions) and runs it. | ||
The next function that is in the ready state (has all its input values available, and is not blocked from sending | ||
its output by other functions) has a job created from its input values and the job is dispatched to be run. | ||
|
||
Executors wait for jobs to run, run them and then return the result, that may or may not contain an output value. | ||
|
||
That consumed the inputs and sends the output value to all functions connected to the output. That makes that input | ||
available to the other function connected to the output, and it may make that other function ready to run. | ||
Any output value is sent to all functions connected to the output of the function that the job ran for. | ||
Sending an input value to a function may make that function ready to run. | ||
|
||
When there are no more functions in the ready to run state, then execution has terminated and the flow ends. | ||
The above is repeated until there are no more functions in the ready state, then execution has terminated and the flow ends. | ||
|
||
### Specific Sequence for this example | ||
Below is a description of what happens in the flor runtime to execute the flow. | ||
|
||
You can see log output (printed to STDOUT and mixed with the number series output) of what is happening using | ||
the `-v, verbosity <Verbosity Level>` command line option to `flowr`. | ||
- Values accepted (from less to more output verbosity) are: `error` (the default), `warn`, `info` `debug` and `trace`. | ||
|
||
#### Init: | ||
* Initial values of 1 are made available in the inputs of "HEAD" and "HEAD-1" values. | ||
* HEAD-1 has input (1) available and is not blocked from sending its outputs, so it is made ready to run. | ||
* HEAD has input (1) available and is not blocked from sending its outputs, so it is made ready to run. | ||
* The "i2" input of the "add" function is initialized with the value 1 | ||
* The "ii" input of the "add" function is initialized with the value 0 | ||
* The "add" function has a value on all of its inputs, so it is set to the ready state | ||
* STDOUT does not have an input value available so it is not "ready" | ||
* SUM does not have its inputs available so it is not "ready" | ||
|
||
#### Loop Starts | ||
ReadyList = HEAD-1(1), HEAD(1) | ||
|
||
Next function with status "ready" is run: | ||
|
||
- HEAD-1 is run with input 1 | ||
- HEAD-1 makes the value 1 available on its output (to STDOUT and SUM) | ||
- HEAD-1 is now blocked from running again until its output to SUM is free | ||
- STDOUT has all inputs available (from "HEAD-1") so is made "ready" | ||
- SUM(1,_) only has one of its inputs available, so it is not made "ready" | ||
|
||
ReadyList = HEAD(1), STDOUT(1) | ||
|
||
Next function with status "ready" is run: | ||
|
||
- "HEAD" is run with input 1 | ||
- This updates its value and makes the value 1 available on its outputs (to HEAD-1 and SUM) | ||
- SUM(1,1) now has both inputs available (from HEAD and HEAD-1) so it is made "function" | ||
- HEAD-1(1) has an input value available (from HEAD, but it cannot run as its output is blocked by SUM, | ||
so it is "blocked on output" and not "ready". | ||
|
||
ReadyList = STDOUT(1), SUM(1,1) | ||
|
||
Next function with status "ready" is run: | ||
|
||
- "STDOUT" runs with input 1. It prints "1" on the stdout of the run-time. | ||
> 1 | ||
ReadyList = SUM(1,1) | ||
|
||
Next function with status "ready" is run: | ||
|
||
- "SUM" runs with inputs 1 and 1. It produces the value 2 on its output (to HEAD) | ||
- SUM running consumes its input and unblocks HEAD-1(1) from running | ||
- HEAD has its input available so is made "ready" with input 2 | ||
|
||
ReadyList = HEAD-1(1), HEAD(2) | ||
|
||
Next function with status "ready" is run: | ||
|
||
- HEAD-1 is run with input 1. It produces 1 on its output (to STDOUT and SUM) | ||
- STDOUT(1) has its input available so is made "ready" | ||
- SUM(1, _) only has one input available and so is not "ready" | ||
|
||
ReadyList = HEAD(2), STDOUT(1) | ||
|
||
Next function with status "ready" is run: | ||
|
||
- "HEAD" is run with input 2. It produces 2 on its output (to HEAD-1 and SUM) | ||
- SUM(1,2) is made "ready" | ||
- HEAD-1(2) is blocked on sending by SUM | ||
|
||
ReadyList = STDOUT(1), SUM(1,2) | ||
|
||
Next function with status "ready" is run: | ||
|
||
- "STDOUT" runs with input 1. It prints "1" on the stdout of the run-time. | ||
> 1 | ||
ReadyList = SUM(1,2) | ||
|
||
Next function with status "ready" is run: | ||
|
||
- SUM runs with inputs 1 and 2. It produces the value 3 on its output (to HEAD) | ||
- HEAD-1(2) has its output unblocked by SUM and so is made "ready" | ||
- HEAD(3) has its input available so is made "ready" | ||
|
||
ReadyList = HEAD-1(2), HEAD(3) | ||
|
||
- HEAD-1(2) is run. It produces 2 on its output (to STDOUT and SUM) | ||
- STDOUT(2) has its input avaialble so is made "ready" | ||
- SUM(2, _) lacks an input and is not ready | ||
|
||
ReadyList = HEAD(3), STDOUT(2) | ||
|
||
- HEAD(3) is run. It produces 3 on its output (to HEAD-1 and SUM) | ||
- SUM(2, 3) is made "ready" | ||
- HEAD-1(3) is blocked on SUM so not "ready" | ||
|
||
ReadyList = STDOUT(2), SUM(2,3), HEAD-1(3) | ||
|
||
Next function with status "ready" is run: | ||
|
||
- STDOUT(2)) runs. It prints "2" on the stdout of the run-time. | ||
> 2 | ||
ReadyList = SUM(2,3) | ||
|
||
Next function with status "ready" is run: | ||
|
||
- SUM(2,3) is run. It produces the value 5 on its output (to HEAD) | ||
- HEAD-1(3) has its output unblocked by SUM and so is made "ready" | ||
- HEAD(5) has its input available so is made "ready" | ||
|
||
ReadyList = HEAD-1(3), HEAD(5) | ||
|
||
Next function with status "ready" is run: | ||
|
||
- HEAD-1(3) is run. It produces 3 on its output (to STDOUT and SUM) | ||
- STDOUT(3) has its input avaialble so is made "ready" | ||
- SUM(3, _) lacks an input and is not ready | ||
|
||
ReadyList = HEAD(5), STDOUT(3) | ||
|
||
Next function with status "ready" is run: | ||
|
||
- HEAD(5) is run. It produces 5 on its output (to HEAD-1 and SUM) | ||
- SUM(3, 5) is made "ready" | ||
- HEAD-1(5) is blocked on SUM so not "ready" | ||
|
||
ReadyList = STDOUT(3), SUM(3,5) | ||
|
||
Next function with status "ready" is run: | ||
|
||
- STDOUT(3)) runs. It prints "3" on the stdout of the run-time. | ||
> 3 | ||
and so on, and so forth.... producing a fibonacci series on the standard output of the run-time: | ||
> 1, 1, 2, 3, 5, 8 ... | ||
Ready = ["add"] | ||
|
||
- "add" runs with Inputs = (0, 1) and produces output 1 | ||
- value 1 from output of "add" is sent to input "i2" of "add" | ||
- "add" only has a value on one input, so is NOT ready | ||
- value 1 from output of "add" is sent to default (only) input of "Stdout" | ||
- "Stdout" has a value on all of its (one) inputs and so is marked "ready" | ||
- input value "i2" (1) of the executed job is sent to input "i1" of "add" | ||
- "add" now has a value on both its inputs and is marked "ready" | ||
|
||
Ready = ["Stdout", "add"] | ||
|
||
- "Stdout" runs with Inputs = (1) and produces no output | ||
- "Stdout" converts the `number` value to a `String` and prints "1" on the STDOUT of the terminal | ||
- "Stdout" no longer has values on its inputs and is set to not ready | ||
|
||
Ready = ["add"] | ||
|
||
- "add" runs with Inputs = (1, 1) and produces output 2 | ||
- value 2 from output of "add" is sent to input "i2" of "add" | ||
- "add" only has a value on one input, so is NOT ready | ||
- value 2 from output of "add" is sent to default (only) input of "Stdout" | ||
- "Stdout" has a value on all of its (one) inputs and so is marked "ready" | ||
- input value "i2" (1) of the executed job is sent to input "i1" of "add" | ||
- "add" now has a value on both its inputs and is marked "ready" | ||
|
||
Ready = ["Stdout", "add"] | ||
|
||
- "Stdout" runs with Inputs = (2) and produces no output | ||
- "Stdout" converts the `number` value to a `String` and prints "2" on the STDOUT of the terminal | ||
- "Stdout" no longer has values on its inputs and is set to not ready | ||
|
||
Ready = ["add"] | ||
|
||
The above sequence proceeds, until eventually: | ||
|
||
- `add` function detects a numeric overflow in the add operation and outputs no value. | ||
- No value is fed back to the "i1" input of add | ||
- "add" only has a value on one input, so is NOT ready | ||
- No value is sent to the input of "Stdout" | ||
- "Stdout" no longer has values on its inputs and is set to not ready | ||
|
||
Ready = [] | ||
|
||
No function is ready to run, so flow execution ends. | ||
|
||
Resulting in a fibonacci series being output to Stdout | ||
``` | ||
1 | ||
2 | ||
3 | ||
5 | ||
8 | ||
...... lines deleted ...... | ||
2880067194370816120 | ||
4660046610375530309 | ||
7540113804746346429 | ||
``` |
This file was deleted.
Oops, something went wrong.