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
を流し続けるので、このように 1
と 2
が出力され続けます。
Advanced: 括弧の付け方
実は、flow では a -> b -> c
は a (-> 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
が優先的に推論されるようになっています。
場合によっては、省略はコードの可読性を下げることにつながるため、注意が必要です。