OOプログラミングに関連しているので、概念データ抽象化を理解しています。しかし、逆に、関数プログラミングは促進または作成しているようです概念の使用制御抽象化。
ブログの周りを検索してみましたが、関連性があり、ダイジェストしやすい(少なくとも私にとって)答えは見つかりませんでした。誰かが関数型プログラミングの文脈でコントロールの抽象化が何を意味するかをいくつかの良い例を使って説明できるかどうか感謝します。
control abstraction のトピックへの1つの参照がここで見つかりましたが、誰かがその概念を要約または言い換えていただければ幸いです。
言語が制御の抽象化をサポートする場合、これは独自の制御フロー構造を定義できることを意味します。これは確かに悪用され、プログラムを完全に理解不能にする可能性がありますが、プログラムをより明確にすることもできます。
制御の抽象化の前提条件は、高次の関数、クロージャー、ラムダですが、多くの抽象化は、ファーストクラスの継続がなければ不便です。オブジェクトはある意味ではクロージャと同等であるため、非機能言語でも制御フローの抽象化のいくつかの側面を使用できますが、通常は構文がより混乱します。
例として、C#のusing
ブロック(またはJavaのtry-with-resource、Pythonのwith
)を考えてみましょう。ここで、言語はこの制御フローを表すために特別な構文を実装しました:
しかし、より高次の関数があれば、これを自分で実装することもできます。
static void using<T>(T resource, Action<T> body)
where T: IDisposable {
try {
body(resource);
} finally {
if (resource != null)
resource.Close();
}
}
これは次のように使用できます:
using(new File("foo.txt"), file => {
...
});
したがって、データを抽象化してユーザー定義のデータ構造を作成できるのと同じように、コントロールを抽象化すると、ユーザー定義の制御構造で言語を進化させることができます。
もう1つの素晴らしい例は、メソッドカスケードオペレーターです。これにより、メソッド呼び出しチェーンでいくつかの副作用を実行できます。
static T Tap<T>(this T instance, Action<T> body) {
body(instance);
return instance;
}
これは無意味に見えますが、オブジェクトを変数または個別の関数に抽出する必要がない場合は、流れるようなインターフェイスでオブジェクトを構成するのに非常に便利です。
return new Foo() // where Foo is a reference type
.Tap(f => f.X = 7)
.Tap(f => f.Y = 18)
.Tap(f => f.Name = "Foo Bar");
コントロールの抽象化には、いくつかの注目すべき使用法があります。
約束 JavaScript(およびC#のタスク/未来)では、深くネストされたコールバックなしで非同期コードを構築できるようにするコントロールの抽象化と見なすことができます。ただし、ファーストクラスの継続がなければ、promiseのコールバックが必要です。継続により、async/await
をライブラリとして実装できます。
Embedded DSLs制御フローの抽象化から大きなメリットを得ます。例えば。 ScalaまたはRubyの柔軟な構文は、このような用途に適しています。たとえば、parser Combinatorライブラリを使用すると、ソースコードでパーサーをアセンブルできますが、入力ドキュメントが解析されるときに、独自の制御フローを使用してこのパーサーを解釈します。
MonadsおよびFunctors HaskellおよびScalaは、データの抽象化と制御の抽象化の両方として解釈できます。単純なモナドは多分またはオプション(類似)これにより、コア言語が変更されるのを待たずにライブラリとして実装できることを除いて、基本的には安全なナビゲーションオペレーター(C#の?.
など)が提供されます。C#では、IEnumerableはSelect
メソッドを使用した少しファンクタのようなものです。
現在、ほとんどの言語は、ある種の制御フロー抽象化をサポートしています。これにより、ユーザーは、不足している演算子を追加するのを待たずに、優れたライブラリを構築して生産性を高めることができます。しかし、いくつかのホールドアウトがあります:
Cは、マクロ以外の制御の抽象化をサポートしていません。理論的には、関数ポインターを使用してクロージャーまたはオブジェクトを実装できますが、これは正確に便利な構文ではありません。
Goがサポートする抽象化の機会はほとんどありません。技術的にはクロージャはありますが、reusable抽象化に必要なジェネリックはありません。組み込み型のみがジェネリックであるため、チャネルとforループを使用していくつかのコントロールの抽象化を再作成できます。
コントロールの抽象化とは、一連の操作を抽象化することであり、通常は関数に抽象化されます。これは関数型プログラミングに固有のものではなく、すべての高水準言語の基本的な機能です。