Basic Syntax of Flow
Basics of Flow
In Flow, you use ->
to describe a unidirectional flow.
a -> b -> c -> d
Example:
> "Hello, world!" -> output
Hello, world!
Hello, world!.
Items connected by ->
are called nodes. In a -> b
, a
is called the upstream node, and b
is the downstream node. Here, "Hello, world!"
is the upstream node, and output
is the downstream node.
The "Hello, world!"
node continuously sends the string Hello, world!
to the downstream node. The output
node displays the data received from the upstream node on the screen.
When data is displayed, the program waits for user input. If Enter
is pressed without typing anything, the program resumes, and the next "Hello, world!"
is displayed again. If the user types something (e.g., .
) and presses Enter
, the program stops.
Multiple Outputs
By using parentheses, you can specify multiple outputs from a single node.
a (-> b) (-> c)
Example:
> 1 -> copy (-> output) (-> + 1 -> output)
1
2
1
2.
The copy
node can have multiple outputs and sends the data from its upstream node to all downstream nodes. One output connects directly to an output
node, while the other connects to + 1
(more on + 1
later).
Since the 1
node continuously sends the value 1
to the copy
node, both 1
and 2
are output repeatedly.
Advanced: Parentheses Usage
In Flow, a -> b -> c
is interpreted as a (-> b (-> c))
. In other words:
"Hello, world!" -> output
is equivalent to
"Hello, world!" (-> output)
and
1 -> copy (-> output) (-> + 1 -> output)
is equivalent to
1 (-> copy (-> output) (-> + 1 (-> output)))
Multiple Inputs
Nodes can also handle multiple inputs.
(a ->) (b ->) c
Example:
> (1 ->) (2 ->) + -> output
3
3.
The +
node receives multiple inputs from upstream nodes and sends the sum downstream.
The +
node supports a variable number of inputs:
> (1 ->) (2 ->) (3 ->) + -> output
6.
> (1 ->) + -> output
1.
Advanced: Input or Output?
The program "Hello, world!" -> output
can be interpreted as either "output
receives the output of "Hello, world!"
" or ""Hello, world!"
is the input of output
." As mentioned earlier, Flow interprets "Hello, world!" -> output
as "Hello, world!" (-> output)
, so "output
receives the output of "Hello, world!"
" is the more syntactically faithful expression.
However, the following produces the same result:
> ("Hello, world!" ->) output
Hello, world!.
Syntactically different, but semantically equivalent (they are the same in Flow's intermediate representation).
Named Outputs and Inputs
You can explicitly name inputs and outputs. For example, consider the following program. The -
node takes two inputs and subtracts them.
> (5 ->) (3 ->) - -> output
2.
Here, 5
is the first argument, and 3
is the second argument.
You can name an input by specifying ->:name
:
> (5 ->:arg1) (3 ->:arg0) - -> output
-2.
Similarly, you can name an output using name:->
(this will be shown later in the if
section).
In fact, all inputs and outputs in Flow have names. If omitted, the system infers the names automatically.
Overall, the structure looks like this. [ ... ]
indicates optional parts:
(... ->[:input1]) ... (... ->[:inputn]) node ([output1:]-> ...) ... ([outputn:]->...)
Conditional Branching: Just Multiple Outputs of a Node
In Flow, conditional branching is handled using the if
node. The if
node sends data to the then
output if the upstream value is True
(and nothing to else
), and to else
otherwise.
> 1 == 1 -> if (then:-> "True!" -> output) (else:-> "False!" -> output)
True!.
terminated
> 1 == 0 -> if (then:-> "True!" -> output) (else:-> "False!" -> output)
False!.
terminated
We haven't explained nodes like "True!"
and "False!"
.
These nodes have an upstream and are called passive immediates. Unlike nodes without upstream (like "Hello, world!" -> output
) which always send values downstream, passive immediates only send their value downstream when they receive an upstream input.
In Flow, control structures like this are treated just as nodes.
When a node has multiple outputs, it does not necessarily send data to all outputs each time it receives an input. Generally, nodes have inputs and outputs, but they do not always behave like functions or multivalued functions.
Note: Omitting Names in if
Nodes
Earlier, we mentioned that names are inferred if omitted. The same applies to if
nodes, so you can omit then:
and else:
:
> 1 == 1 -> if (-> "True!" -> output) (-> "False!" -> output)
True!.
In the interpreter, then
is inferred preferentially.
However, omitting names can sometimes reduce code readability, so this is not recommended.