オブジェクト指向言語を検討する:
オブジェクト指向プログラミングのバックグラウンドを持っているほとんどの人は、Javaの本質を捉えたさまざまな言語の一般的で直感的なインターフェースに精通しています Collection
&- List
インターフェース。 Collection
は、必ずしも自然な順序付け/インデックス付けを備えていないオブジェクトのコレクションを指します。 List
は、自然な順序付け/索引付けを持つコレクションです。これらのインターフェイスは、他の言語の同等のインターフェイスと同様に、Javaの多くのライブラリデータ構造を抽象化します。ほとんどのライブラリデータ構造を効果的に使用するには、これらのインターフェイスを深く理解する必要があります。
Haskellへの移行:
Haskellには、オブジェクトのインターフェースと同様に型に作用する型クラスシステムがあります。 Haskellは、型が機能を考慮している場合、ファンクター、アプリケーション、モナドなどに関して 適切に設計された型クラス階層 を持っているようです。彼らは明らかに 正しくてよく抽象化された型クラス を望んでいます。しかし、多くのHaskellのコンテナを見ると( List
、 Map
、 Sequence
、 Set
、 Vector
)それらはほとんどすべて非常に類似した(または同一の)関数を持っていますが、型クラスによって抽象化されていません。
いくつかの例:
null
「空」をテストするためlength
/size
要素数elem
/member
セットを含める場合empty
および/またはsingleton
デフォルトの構築union
for set union(\\)
/diff
セット差(!)
/(!!)
安全でない索引付け(部分関数)(!?)
/lookup
安全なインデックス作成(関数全体)上記の関数のいずれかを使用したいが、2つ以上のコンテナーをインポートした場合は、インポートしたモジュールから関数を非表示にするか、モジュールから必要な関数のみを明示的にインポートするか、インポートしたモジュールを修飾する必要があります。しかし、すべての関数が同じ論理機能を提供するため、面倒なように思えます。関数が各モジュールで個別にではなく型クラスから定義されている場合、コンパイラの型推論メカニズムでこれを解決できます。また、型クラスを共有している限り、基になるコンテナーの切り替えが簡単になります(つまり、ランダムアクセスの効率を高めるためにSequence
の代わりにList
を使用できます)。
Haskellにこれらの関数のいくつかを統合および一般化するためのCollection
および/またはIndexable
型クラスがないのはなぜですか?
部分的には、モナドと矢印がHaskellの新しい革新的な機能であるのに対し、コレクションは比較的平凡であるためです。 Haskellは研究言語として長い歴史があります。興味深いリサーチクエスチョン(モナドインスタンスの設計とモナドのジェネリック操作の定義)は、「産業強度」の研磨(コンテナAPIの定義)よりも多くの開発努力を要します。
部分的には、これらのタイプが3つの異なるパッケージ(ベース、コンテナー、およびベクター)からのものであり、3つの別個の履歴と設計者があるためです。そのため、設計者が単一の型クラスのインスタンスを提供する際の調整が難しくなります。
部分的には、その理由は、あなたが言及した5つのコンテナーすべてをカバーする単一の型クラスを定義することが非常に難しいためです。 List、Sequence、Vectorは比較的似ていますが、MapとSetには完全に異なる制約があります。 List、Sequence、Vectorの場合、単純なコンストラクタークラスが必要ですが、Setの場合は、要素タイプにOrdインスタンスが必要なため、機能しません。さらに悪いことに、Mapはほとんどのメソッドをサポートできますが、そのシングルトン関数には2つのパラメーターが必要であり、残りのパラメーターは1つだけです。
他の回答が指摘しているように、Haskellは異なる語彙を使用する傾向があります。しかし、違いについては理由をうまく説明していないと思います。
Javaのような言語では、関数は「第一級市民」ではありません。匿名関数が最新バージョンで利用できるのは事実ですが、このスタイルのインターフェース(Collection、Indexable、Interableなど)はそれ以前に設計されました。
これにより、コードを渡すのが面倒になるため、他の人のデータをコードに渡すことをお勧めします。例えば:
Iterable
を実装するデータにより、s write for (Foo x : anIterable) { ... }
ArrayAccess
を実装するデータにより、s書き込みanArrayAccess[anIndex]
このスタイルは、ジェネレーターを実装するOO言語でも見られます。これは、sがfor yieldedElement in aGenerator: ...
を記述する別の方法だからです。
Haskellはその型クラスで異なるアプローチを取ります:私たちは私たちのコードが他の人のデータに渡されることを好みます。いくつかの(簡略化された)例:
Functor
sはコードを受け入れ、「含まれている」すべての要素に適用しますMonad
sはコードを受け入れ、ある種の「シーケンス」で適用しますFoldable
sはコードを受け入れ、それを使用してコンテンツを「削減」しますweはourIterable
ループでコードを呼び出す必要があるため、Javaはfor
のみを必要とします。これにより、正しく呼び出されることを確認できます。 他の誰かのコードが私たちを呼び出すので、Haskellはより具体的な型クラスを必要とします。したがって、howを呼び出す必要があります。 map
、fold
、unfold
などですか?
ありがたいことに、型システムは私たちが正しい方法を選択するのに役立ちます;)
lens
パッケージは、この一部を提供します。
空のテスト、空のコンテナの作成これらは両方とも Control.Lens.Empty
のAsEmpty
型クラスによって提供されます=。
キー/インデックスによる要素へのアクセス。 Control.Lens.At
のAt
およびIxed
型クラス。
セットのようなコンテナのメンバーシップを確認しています。 Control.Lens.At
のContains
型クラス。
シーケンスのようなコンテナへの要素の追加と削除。 Control.Lens.Cons
のCons
およびSnoc
型クラス。
また、pure
タイプクラスのApplicative
メソッドは、「シングルトン」コンテナを作成するためによく使用されます。 Set
のように、Haskellのファンクター/アプリケーションではないものについては、おそらくpoint
from Data.Pointed
を使用できます。
Haskellには、基本パッケージのコレクションを操作するためのいくつかの型クラスがあります。 Functor 、 Foldable および Traversable は、コレクションの操作に役立ちます。 Monoid 、 Applicative および/または Alternative 型クラスはコレクションの構築に役立ちます。
一緒に、これらのクラスは質問で言及された操作のほとんどをカバーしますが、よりコンテナ固有の関数よりも効率が悪いかもしれません(これらの多くはクラスメソッドであり、そのデフォルト定義は必要に応じてオーバーライドできます)。
null
「空」をテストするため
base4.8(any (const True)
は以前のバージョンの代替)であるため、Foldableはnull
をサポートします。
要素数の長さ/サイズ:
base4.8(getSum . foldMap (const 1)
は以前のバージョンの代替)であるため、Foldableはlength
をサポートします。
セットを含めるためのelem/member
折りたたみ式はelem
、notElem
およびmember
をサポートします。
デフォルトの構造の場合は空および/またはシングルトン
空の場合、Monoidのmempty
とAlternativeのempty
があります。シングルトンの場合、Applicativeからpure
があります。
セットユニオンのユニオン
Monoidからはmappend
、Alternativeからは<|>
があります。それらは必ずしも集合和集合を実装するわけではありませんが、空と、通常はシングルトンと検索と一緒にうまく機能する何らかの形の和集合を実装します。
(\)/セット差の差分
残念ながら、これはサポートされていません。
(!)/(!!)安全でない索引付け(部分関数)
fromJust
を、安全なインデックス作成のための関数と一緒に使用できます。
(!?)/安全な索引付けの検索(全機能)
Foldableからfind
があります。
このような型クラスは標準のHaskellに存在しますが、同等のOO対応するものと同じ名前ではありません。たとえば、Collection
型クラスはHaskellではFoldable
と呼ばれます。これを使用してテストできます構造が空の場合(foldr (const False) True x
)、要素の数をカウントする場合(foldMap (const 1) x
)、またはセットのメンバーシップをテストする場合(foldr (\e' present -> (e==e') || present) False x
for some e
)。
要素ルックアップなどの操作の場合、シーケンシャルデータで機能する可能性のあるArray
タイプクラスがあります。柔軟性を高めるために、たとえば次のような独自のIndexable
クラスを作成できます(レンズに注意してください)。
class Indexable m k a where
at :: k -> Lens' m (Maybe a)
Null要素と集合和集合はMonoid
型クラスに属します(ここでmappend == union
)。この観点から、セットの違いは独自の型クラスDifferentiable
(数十のHaskellライブラリにすでに存在していると確信しています)に実装することもでき、命令型言語と完全に互換性があります。
Haskellは、数学者などによって設計されているため、他のほとんどの言語と同じ語彙を使用していませんが、安心してください。素晴らしい言語であることに加えて、実用的な言語ではないという意味ではありません:-)
法律。良い型クラスには法則があります。優れた型クラスは十分なパラメトリシティを持っているので、その法則は「無料の定理」です。法則のない型クラスは、アドホックな名前のオーバーロードです。
また、 classy-prelude および Edison-API も確認してください。
さまざまなコレクションの側面に対応する型クラスがあります。
構成:モノイド(モジュールData.Monoid)
シーケンシャルコントロール:Applicative、Monad(モジュールControl.Applicative、Control.Monad)
シーケンシャルコンポジション:Alternative、MonadPlus(モジュールControl.Applicative、Control.Monad)
非シーケンシャルマッピングとリダクション:Functor(mod。Data.Functor)、Foldable(mod。Data.Foldable)
シーケンシャルマッピングとリダクション:トラバーサブブル(モジュールData.Traversable)
シリアル化:バイナリ(mod。Data.Binary)
比較:Eq、Ord(mod。Data.Eq、Data.Ord)
テキスト化:表示、読み取り
深い評価(通常の形式へ):NFData(mod。Control.DeepSeq)
一般的なデータ型のトラバーサビリティ:データ(mod。Data.Data)
モノモーフィックコレクション(ByteString、IntSet、Text)がFunctorとFoldableを実装できないことを除いて(タイプarity == 1(種類:*-> *)が必要です)
また どちらの(Set a)もFunctorを実装していません 。
パッケージ mono-traversable は、単形型を除外せずに一部のクラスを再定義します。
更新。パッケージ mono-traversable および classy-prelude を使用して、ほとんどの関数を型クラスに配置しようとしています。