LukePalmerが実存型クラスのアンチパターンと呼んでいるものに向かっているようです。まず、AIを再生するデータ型がいくつかあると仮定しましょう。
data AIPlayerGreedy = AIPlayerGreedy { gName :: String, gFactor :: Double }
data AIPlayerRandom = AIPlayerRandom { rName :: String, rSeed :: Int }
ここで、これらを型クラスの両方のインスタンスにすることで、これらを操作したいとします。
class AIPlayer a where
name :: a -> String
makeMove :: a -> GameState -> GameState
learn :: a -> GameState -> a
instance AIPlayer AIPlayerGreedy where
name ai = gName ai
makeMove ai gs = makeMoveGreedy (gFactor ai) gs
learn ai _ = ai
instance AIPlayer AIPlayerRandom where
name ai = rName ai
makeMove ai gs = makeMoveRandom (rSeed ai) gs
learn ai gs = ai{rSeed = updateSeed (rSeed ai) gs}
これは、そのような値が1つしかない場合に機能しますが、気付いたように、問題が発生する可能性があります。しかし、型クラスはあなたに何を買いますか?あなたの例では、のさまざまなインスタンスのコレクションをAIPlayer
均一に処理したいとします。コレクションに含まれる特定のタイプがわからないため、gFactor
またはrSeed
;のようなものを呼び出すことはできません。によって提供されるメソッドのみを使用できるようになりますAIPlayer
。したがって、必要なのはこれらの関数のコレクションだけであり、これらを単純な古いデータ型にパッケージ化できます。
data AIPlayer = AIPlayer { name :: String
, makeMove :: GameState -> GameState
, learn :: GameState -> AIPlayer }
greedy :: String -> Double -> AIPlayer
greedy name factor = player
where player = AIPlayer { name = name
, makeMove = makeMoveGreedy factor
, learn = const player }
random :: String -> Int -> AIPlayer
random name seed = player
where player = AIPlayer { name = name
, makeMove = makeMoveRandom seed
, learn = random name . updateSeed seed }
つまりAIPlayer
、は、その名前、行動を起こす方法、新しいAIプレーヤーを学び、生産する方法などのノウハウの集まりです。データ型とそのインスタンスは、単にAIPlayer
sを生成する関数になります。[greedy "Alice" 0.5, random "Bob" 42]
よく型付けされているように、すべてを簡単にリストに入れることができます。それは型[AIPlayer]
です。
あなたは、それは本当です、あなたの最初のケースを実存的なタイプでパッケージ化することができます:
{-# LANGUAGE ExistentialQuantification #-}
data AIWrapper = forall a. AIPlayer a => AIWrapper a
instance AIWrapper a where
makeMove (AIWrapper ai) gs = makeMove ai gs
learn (AIWrapper ai) gs = AIWrapper $ learn ai gs
name (AIWrapper ai) = name ai
さて、[AIWrapper $ AIPlayerGreedy "Alice" 0.5, AIWrapper $ AIPlayerRandom "Bob" 42]
よくタイプされています:それはタイプ[AIWrapper]
です。しかし、上記のLuke Palmerの投稿が観察しているように、これは実際には何も購入せず、実際、あなたの生活をより複雑にします。これは、より単純な型クラスのない場合と同等であるため、利点はありません。実存は、まとめる構造がより複雑な場合にのみ必要です。