私にとって重要な要素は、次の質問に対する答えです。
データ型の構造は外の世界に関連していますか?
たとえば、リスト データ型の内部構造は、外部の世界と非常に関連性があります。これは、消費者に公開するのに非常に役立つ帰納的な構造を持っています。これは、リストの構造の帰納によって進む関数を構築するためです。リストが有限である場合、これらの関数は終了することが保証されます。また、このように関数を定義すると、ここでも帰納法によって、関数に関するプロパティを簡単に提供できます。
対照的に、Set
データ型を抽象化するのが最善です。containers
内部的には、パッケージ内のツリーとして実装されています。ただし、配列を使用して実装するか、(機能的な設定でより便利に) 構造がわずかに異なり、さまざまな不変条件 (バランスまたはアンバランス、分岐係数など) を尊重するツリーを使用して実装することもできます。ちなみに、コンストラクターが型を介して既に適用している不変条件を超えて不変条件を適用する必要があるため、データ型を具象にすることはできません。
リストの例とセットの例の本質的な違いは、Set
データ型がで可能な操作にのみ関連することSet
です。標準ライブラリはすでにリストに作用する多くの関数を提供しているため、リストは関連していますが、さらにそれらの構造も関連しています。
補足として、実際にはリストの帰納的構造 (終了と動作を簡単に推論できる関数を作成する上で非常に基本的) が、リストを消費する 2 つの関数によって抽象的に捉えられていることに反対する人もいるかもしれません:foldr
とfoldl
. これら 2 つの基本的なリスト演算子があると、ほとんどの関数はリストの構造を検査する必要がまったくないため、リストを抽象化しすぎる可能性があると主張できます。Traversable
この引数は、すべての構造体、すべての構造体など、他の多くの同様の構造に一般化されますFoldable
。ただし、リストで考えられるすべての再帰パターンをキャプチャすることはほとんど不可能であり、実際、多くの関数はまったく再帰的ではありません。foldr
とだけが与えられfoldl
たhead
たとえば、非常に面倒ですが、まだ可能です。
head xs = fromJust $ foldl (\b x -> maybe (Just x) Just b) Nothing xs
リストの内部構造だけを公開する方がはるかによいでしょう。
最後のポイントの 1 つは、データ型の実際の表現が外部の世界と関係がない場合があることです。これは、ある種の最適化されたものであり、正規の表現ではない可能性がある、または単一の「正規の」表現がないなどの理由によるものです。このような場合、データ型を抽象化したままにし、データ型の「ビュー」を提供する必要があります。これにより、パターン マッチングが可能な具体的な表現が提供されます。
Complex
1 つの例は、複素数のデータ型を定義する場合です。この場合、デカルト形式と極形式の両方を標準と見なすことができます。この場合、Complex
抽象的に保ちますが、2 つのビュー、つまり関数polar
とをエクスポートします。これらのビューcartesian
はそれぞれ、長さと角度のペア、またはデカルト平面の座標を返します。