diff --git a/functional-programming-lean/src/SUMMARY.md b/functional-programming-lean/src/SUMMARY.md index 3016ef0..00fd64c 100644 --- a/functional-programming-lean/src/SUMMARY.md +++ b/functional-programming-lean/src/SUMMARY.md @@ -50,7 +50,7 @@ - [モナド組み立てキット](monad-transformers/transformers.md) - [モナド変換子の順序](monad-transformers/order.md) - [さらなる `do` の機能](monad-transformers/do.md) - - [Additional Conveniences](monad-transformers/conveniences.md) + - [その他の便利な機能](monad-transformers/conveniences.md) - [Summary](monad-transformers/summary.md) - [Programming with Dependent Types](dependent-types.md) - [Indexed Families](dependent-types/indexed-families.md) diff --git a/functional-programming-lean/src/monad-transformers/conveniences.md b/functional-programming-lean/src/monad-transformers/conveniences.md index ed7ea8b..58ba985 100644 --- a/functional-programming-lean/src/monad-transformers/conveniences.md +++ b/functional-programming-lean/src/monad-transformers/conveniences.md @@ -1,49 +1,105 @@ + +# その他の便利な機能 + + + +## パイプ演算子 + +関数は通常、引数の前に書かれます。これはプログラムを左から右に読む際に、関数の **出力** が最重要だという見方を助長します。つまり、関数は達成すべきゴール(つまり計算すべき値)を持っており、そのプロセスをサポートするために引数を受け取るという視点です。しかし、プログラムの中には出力の生成のために入力が逐次的に改良されていく、という観点のほうが理解しやすいものも存在します。このような状況に対して、LeanはF#にあるものと似た **パイプライン** 演算子を提供しています。パイプライン演算子はClojureのスレッディングマクロと同じ状況化で有用です。 + + + +パイプライン `{{#example_in Examples/MonadTransformers/Conveniences.lean pipelineShort}}` は `{{#example_out Examples/MonadTransformers/Conveniences.lean pipelineShort}}` の省略形です。例えば、以下を評価すると: + ```lean {{#example_in Examples/MonadTransformers/Conveniences.lean some5}} ``` + + +以下の結果になります: + ```output info {{#example_out Examples/MonadTransformers/Conveniences.lean some5}} ``` + + +このように強調箇所を変えることでより読みやすくなるプログラムもありますが、多くのコンポーネントを含む場合にパイプラインはその本領を発揮します。 + + +以下の定義に対して: + ```lean {{#example_decl Examples/MonadTransformers/Conveniences.lean times3}} ``` + + +以下のパイプラインは: + ```lean {{#example_in Examples/MonadTransformers/Conveniences.lean itIsFive}} ``` + + +以下を出力します: + ```output info {{#example_out Examples/MonadTransformers/Conveniences.lean itIsFive}} ``` + +より一般的には、パイプラインの列 `{{#example_in Examples/MonadTransformers/Conveniences.lean pipeline}}` は関数適用のネスト `{{#example_out Examples/MonadTransformers/Conveniences.lean pipeline}}` の省略形です。 + + + +パイプラインは反対向きに書くこともできます。この場合、変換する対象のデータを先に持ってきません;しかし、入れ子になった括弧が多くて読者が困るような場合にはパイプラインによって適用のステップを明確にすることができます。先ほどの例は以下の記述と等価です: + ```lean {{#example_in Examples/MonadTransformers/Conveniences.lean itIsAlsoFive}} ``` + + +これは以下の短縮形です: + ```lean {{#example_in Examples/MonadTransformers/Conveniences.lean itIsAlsoFiveParens}} ``` + + +Leanのメソッドのドット記法は、ドットの前の型名を使ってドットの後ろの演算子の名前空間を解決するもので、パイプラインと似た目的を提供しています。仮にパイプライン演算子が無くとも、`{{#example_in Examples/MonadTransformers/Conveniences.lean listReverse}}` の代わりに `{{#example_out Examples/MonadTransformers/Conveniences.lean listReverse}}` と書くことは可能です。しかし、パイプライン演算子はドットを付けた関数をたくさん使う場合でも便利です。`{{#example_in Examples/MonadTransformers/Conveniences.lean listReverseDropReverse}}` は `{{#example_out Examples/MonadTransformers/Conveniences.lean listReverseDropReverse}}` と書くこともできます。この書き方では、ただ引数を受け取るという理由だけで式を括弧で囲む必要がなく、KotlinやC#のような言語での便利なメソッドチェーンを再現しています。ただし、この場合は名前空間を手動で指定する必要があります。そこで究極的な便利機能として、Leanは「パイプラインドット」演算子を提供しています。これは関数をパイプラインのようにグループ化しますが、名前空間を解決するために型名を使用します。「パイプラインドット」を使用すると、上記の例は `{{#example_out Examples/MonadTransformers/Conveniences.lean listReverseDropReversePipe}}` と書き換えることができます。 + +## 無限ループ + + + +`do` ブロックの中で、`repeat` キーワードを使うと無限ループを作れます。例えば、`"Spam!"` という文字列を連投するプログラムで以下のように使われます: + ```lean {{#example_decl Examples/MonadTransformers/Conveniences.lean spam}} ``` + + +`repeat` ループは `for` ループと同じように `break` と `continue` をサポートしています。 + + +[`feline` の実装](../hello-world/cat.md#streams) における `dump` 関数は再帰関数を使って永遠に実行していました: + ```lean {{#include ../../../examples/feline/2/Main.lean:dump}} ``` + + +この関数は `repeat` を使うことで劇的に短縮できます: + ```lean {{#example_decl Examples/MonadTransformers/Conveniences.lean dump}} ``` + +`spam` も `dump` も、それ自体が無限再帰ではないため `partial` として宣言する必要はありません。その代わりに、`repeat` は `ForM` インスタンスが `partial` である型を使用します。関数の部分性は、関数の呼び出し時に「感染」することはありません。 + + + +## whileループ + + +局所的な可変性を使ってプログラムを書く場合、`while` ループは `if` で `break` をガードするような `repeat` よりも便利です: + ```lean {{#example_decl Examples/MonadTransformers/Conveniences.lean dumpWhile}} ``` + +裏では、`while` は `repeat` をよりシンプルにした表記にすぎません。