数週間、オブジェクト間の関係について考えてきました。特にOOPのオブジェクトについては考えていません。たとえばC ++では、他のオブジェクトへのアクセスを必要とする構造内でポインターまたはポインターのコンテナーを階層化することでそれを表現することに慣れています。オブジェクトAがにアクセスする必要がある場合、in Bを見つけることは珍しいことではありません。B *pBA
しかし、私はもはやC ++プログラマーではありません。関数型言語を使用してプログラムを作成します。特に、純粋な関数型言語であるHaskellを使用します。ポインター、参照、またはそのようなものを使用することは可能ですが、「Haskell以外の方法で行う」など、私はそれに不思議に思います。
それから私はそれらすべての関係のことについてもう少し深く考え、要点に達しました:
「なぜこのような関係をレイヤー化して表現するのですか?
私はすでにそれについて考えている人々を読んだ(ここ)。私の見解では、明示的なグラフを介して関係を表現する方が、タイプのコアに焦点を合わせ、後でコンビネータを介して関係を表現できるため、はるかに優れています(SQLと少し似ています)。
コア私たちは定義するときにことを意味しA、我々は何を定義するために期待Aされるで作られたそれはない、何に依存します。たとえば、ビデオゲームでは、タイプがある場合Character、それについて話すことTraitは合法です、Skillまたはそのようなことですが、私たちが話すWeaponかItemsどうかはそうですか?もうよくわかりません。次に:
data Character = {
chSkills :: [Skill]
, chTraits :: [Traits]
, chName :: String
, chWeapon :: IORef Weapon -- or STRef, or whatever
, chItems :: IORef [Item] -- ditto
}
私にとってデザインの点で本当に間違っているように思えます。私はむしろ次のようなものを好みます:
data Character = {
chSkills :: [Skill]
, chTraits :: [Traits]
, chName :: String
}
-- link our character to a Weapon using a Graph Character Weapon
-- link our character to Items using a Graph Character [Item] or that kind of stuff
さらに、新しい機能を追加する日が来ると、新しいタイプ、新しいグラフ、リンクを作成できます。最初の設計では、Character型を壊すか、何らかの回避策を使用して拡張する必要があります。
その考えについてどう思いますか?純粋な関数型言語であるHaskellでこのような問題に対処するのに最適な方法は何だと思いますか?
Character すべき武器を保持することができます。からCharactersへWeaponsのマップがあり、そのマップにキャラクターがいない場合、それはキャラクターが現在武器を保持していないか、キャラクターが武器をまったく保持できないことを意味しますか?さらに、が武器を保持できるかどうかがアプリオリにわからないため、不必要な検索を行うことになりますCharacter。
canHoldWeapons :: Boolフラグを付けて、武器を保持できるかどうか、そしてキャラクターがいないかどうかをすぐに知らせることは意味がないでしょうグラフでは、キャラクターが現在武器を保持していないと言えます。作るgiveCharacterWeapon :: WeaponGraph -> Character -> Weapon -> WeaponGraphだけのように振る舞うidというフラグがあればFalse提供の文字、または使用のためにMaybe失敗したことを表現します。
-Wall、GHCで警告を発行しませんdata Foo = Foo { foo :: Int } | Bar { bar :: String }。次に、チェックを入力してfoo (Bar "")or を呼び出しますbar (Foo 0)が、どちらも例外が発生します。位置スタイルデータコンストラクターを使用している場合、コンパイラーはアクセサーを生成しないため問題はありませんが、大きな構造のレコードタイプの利便性は得られず、使用するボイラープレートが多くなります。レンズのようなライブラリ。