構文
flow の基本構文

flow の基本構文

流れの基本

flow では -> を用いて単方向の流れを記述します。

a -> b -> c -> d

例:

> "Hello, world!" -> output
Hello, world!
Hello, world!.

-> でつなぎ合わされているものを、ノードと呼びます。また、a -> b において、 a を上流ノード、 b を下流ノードと呼びます。ここでは "Hello, world!" が上流ノード、 output が下流ノードです。

"Hello, world!" ノードは、 Hello, world! という文字列を下流ノードに流し続けます。outputノードは、上流ノードから流れてきたデータを画面に出力します。

画面にデータが出力された際、プログラムはユーザーの入力を待ちます。何も入力されずに Enter が押された際は、プログラムが再開されます。この場合は、次に流れてきた Hello, world! という文字列が再び出力されます。ユーザーが何か(. など)入力して Enter が押された際、プログラムは停止します。

複数の出力

括弧を使うことで、1 つのノードからの複数の出力に対して処理を記述できます。

a (-> b) (-> c)

例:

> 1 -> copy (-> output) (-> + 1 -> output)
1
2
1
2.

copy ノードは複数の出力を持つことができ、上流ノードから流れてきたデータを下流すべてに流します。片方は output ノードに直接つながっていますが、もう片方には + 1 がつながっています(+ 1 については後ほど説明します)。

1 ノードは copy ノードに値 1 を流し続けるので、このように 12 が出力され続けます。

Advanced: 括弧の付け方

実は、flow では a -> b -> ca (-> b (-> c)) と解釈されています。つまり、

"Hello, world!" -> output

は、

"Hello, world!" (-> output)

であり、

1 -> copy (-> output) (-> + 1 -> output)

1 (-> copy (-> output) (-> + 1 (-> output)))

となります。

複数の入力

ノードは、複数の入力に対応することもできます。

(a ->) (b ->) c

例:

> (1 ->) (2 ->) + -> output
3
3.

+ ノードは複数の入力を上流から受け取り、すべてを足した和を下流に流します。

+ ノードでは入力の数は可変です。

> (1 ->) (2 ->) (3 ->) + -> output
6.
> (1 ->) + -> output
1.

Advanced: 入力か出力か

"Hello, world!" -> output というプログラムは、「output"Hello, world!" の出力」とも「"Hello, world!"output の入力」ともとらえることができます。先ほど説明したように、flow では、"Hello, world!" -> output"Hello, world!" (-> output) と解釈されるので「output"Hello, world!" の出力」がより構文に忠実な表現となります。

ですが、

> ("Hello, world!" ->) output
Hello, world!.

も実行結果は同じです。実は、構文的には異なりますが、意味論は一致します(flow の中間表現ではこの 2 つは同一となります)。

名前付き出力・入力

入力・出力に名前を明示することができます。例えば、次のプログラムを実行してみましょう。 - は、2 つの入力を受け取り引き算をするノードです。

> (5 ->) (3 ->) - -> output
2.

5 が第一引数、3 が第二引数となっています。

->:name と指定することで、入力に名前を付けることができます。

> (5 ->:arg1) (3 ->:arg0) - -> output
-2.

同様に、 name:-> と指定することで、出力に名前を付けられます(この例は後の if で見ることにします)。

実は、flow のすべての入力・出力には名前がついており、省略された場合は処理系によって名前が推論されています。

全体として、次のような構造になります。 [ ... ] は省略可能であることを表しています。

(... ->[:input1]) ... (... ->[:inputn]) node ([output1:]-> ...) ... ([outputn:]->...)

条件分岐: 単なるノードの複数の出力

flow では、条件分岐は if というノードにより行います。if ノードは、上流のデータが True であれば then 出力にデータを流します( else にはデータは流れません)。そうでなければ、else 出力にデータを流します。

> 1 == 1 -> if (then:-> "True!" -> output) (else:-> "False!" -> output)
True!.
terminated
> 1 == 0 -> if (then:-> "True!" -> output) (else:-> "False!" -> output)
False!.
terminated

ここで出てきている "True!" "False!" のようなノードについて説明をしていませんでした。
これらのノードは、上流を持っており、 消極的即値(passive immediate) と呼んでいます。上流のない "Hello, world!" -> output の時のように、常に値を下流に流すのではなく、上流から値が流れてきたときに初めて下流にその値を流します。

flow では、このような制御構造も単なるノードとして扱われます。 出力が複数ある場合、入力が来るごとにすべての出力にデータが流れるとは限りません。一般に、ノードは入力と出力を持ちますが、関数や多価関数のようには振る舞わないこともあります。

Note: if ノードにおける名前の省略

先ほど、「名前の指定がなければ、処理系が名前を推論する」と書きました。これは if でも同様で、次のように then: else: は省略できます。

> 1 == 1 -> if (-> "True!" -> output) (-> "False!" -> output)
True!.

処理系では、 then が優先的に推論されるようになっています。

場合によっては、省略はコードの可読性を下げることにつながるため、注意が必要です。