2013-05-22 10 views
7

私は今この少しsoccer gameを書いています、そして、最初からバグがあることが1つあります。ゲームはYampa Arcadeのパターンに従うので、「オブジェクト」のサムタイプは、ゲーム中にあります:タイプチェッカーで私を助けてくれますか?タイプファミリーで、多分?

data ObjState = Ball Id Pos Velo 
       | Player Id Team Number Pos Velo 
       | Game Id Score 

オブジェクトがメッセージに反応するので、別の合計の種類があります:

data Msg = BallMsg BM 
     | PlayerMsg PM 
     | GameMsg GM 
data BM = Gained | Lost 
data PM = GoTo Position | Shoot 
data GM = GoalScored | BallOutOfBounds 

ヤンパフレームワークが依存しているがいわゆる信号機能について説明する。私たちの場合、ボール、プレーヤ、ゲームの動作のための信号関数があります。大幅に簡略化:

ballObj, playerObj, gameObj :: (Time -> (GameInput, [Msg])) 
           -> (Time -> (ObjState, [(Id, Msg)])) 

ballObjは、ゲームインプット(キーストローク、ゲーム状態など)を生成する関数と、特定の時間にボールのメッセージのリストを取得し、ボールの状態を返す関数を返し、他のオブジェクト(ボール、ゲーム、選手)を選ぶことができます。

ballObj, playerObj, gameObj :: SF (GameInput, [Msg]) (ObjState, [(Id, Msg)]) 

この均一な型シグネチャは、ヤンパフレームワークのために重要である:(再び、非常に大雑把に単純化された)、それは11 + 11のリストから大きな信号機能を構築ヤンパでは、型シグネチャは、実際には少し立派に見えます(プレイヤー)+ 1(ボール)+ 1(ゲーム)の信号機能が同じタイプ(dpSwitch経由)で実行されます(反応します)。

これで、どのようなバグですか?BallMsgをボールに、またはPlayerMsgをプレーヤに送信するだけです。誰かがGameMsgをBallに送ると、プログラムがクラッシュします。これを避けるために型チェッカーを入手する方法はありませんか?私は最近、タイプファミリーについてのこの素晴らしいPokemon投稿を読んでいます。それはいくつかの類推のようです。だから、多分これが出発点であるかもしれない:

class Receiver a where 
    Msg a :: * 
    putAddress :: Msg a -> a -> Msg a 

data BallObj = ... 
data GameObj = ... 
data PlayerObj = ... 

instance Receiver BallObj where 
    Msg BallObj = Gained | Lost 
(...) 

、SFの機能は、次のようになります。

forall b . (Receiver a, Receiver b) => SF (GameInput, [Msg a]) (a, [(b, Msg b)]) 

これはどこでも私を得るのだろうか?

+0

リラックス。 Haskellプログラムはタイプエラーのためクラッシュすることはありません(安全でないコードや外部コードを使用しない限り)。問題を示す作業コードがありますか? –

+3

メッセージを処理する関数内の非網羅的なパターンのためにクラッシュします。 – martingw

+0

ああ、その種のクラッシュ。 –

答えて

1

あなたのデザインに大きな問題が1つあります。全く異なるエンティティBall,PlayerGameを1つのタイプで統一してください。あなたはそれらのエンティティを超える組合の種類が必要な場合は、それらを別々の種類を作ることによって、あなたがメッセージを持って同じ道を行くすなわち:あなたが特定の機能の両方を発現することができるでしょう

data AnyObject = AnyObjectBall Ball 
       | AnyObjectPlayer Player 
       | AnyObjectGame Game 

この道(Ball -> BallMsg -> ...)一般的なもの(AnyObject -> AnyMsg -> ...)です。

しかし、私が正しくあなたの問題を理解していれば、私は労働組合の種類を必要としないあなたのためのソリューションを持っていると思う:

class Signal object message where 
    signal :: SF (GameInput, [message]) (object, [(Id, message)]) 

data Ball = Ball Id Pos Velo 
data BallMsg = BallMsgGained | BallMsgLost 
instance Signal Ball BallMsg where 
    -- ... 

-- so on for Player and Game 
+0

ありがとう、それはこれらの行に沿って何かでなければなりません。しかし、オブジェクトとメッセージの間にある種の機能的依存関係やタイプファミリー関係が必要であり、SFの結果で受け取るオブジェクトでメッセージをタプルする必要があると思います。それはまだ動作しないか、または非常に不器用なように見えるかもしれません。なぜなら、メッセージの生成とディスパッチは静的な型付けにはうまくいかない動的な性質を持っているからです。私が何かを管理するなら、私はここでそれを共有します! – martingw

+0

@martingwランタイムでメッセージまたはオブジェクトのタイプが定義されている場合は、タイプファミリや他のタイプレベルのプログラミング機能が役立ちます。このシナリオでは、 'AnyObject - > AnyMsg - > ...'型の関数でパターンマッチングすることをお勧めします。 –

+0

私はいくつかの静的なタイピングの機会があると思います:例えばボールSFは常にボールの位置を測定し、ボールが範囲外であればゲームSFに「範囲外」メッセージを送信し、あなたは今ボールを持っているプレイヤーにメッセージを送ります。ゲームのIDが2であり、プレイヤーのIDが4であるとすると、ボールSFはリスト[(2、OutOfBounds)、(4、DropPossession)]を返す。私が数字を混ぜると、プログラムがクラッシュします。オブジェクトとメッセージタイプの間に資金を入れて、[(Game、OutOfBounds)、(Player、DropPossession)]のようなものとすることができます... – martingw

2

yampaアーケードペーパーをスキミングすると、その例のようにrouteという関数があるようです。

私は、routeを1つのゲームオブジェクト、1つのボールオブジェクト、プレーヤーオブジェクトのコレクションという単一のリストではなく変更するように提案します。その後、均一なAnyMsg

data BallMsg = ... 
data PlayerMsg = ... 
data GameMsg = ... 

data AnyMsg = ABallMsg BallMsg 
      | APlayerMsg PlayerMsg 
      | AGameMsg GameMsg 

route作品を持っているが、それは、その内容に応じて、正しい宛先にディスパッチします。

+0

入力してくれてありがとう!私のコードは既にあなたのように整理されているようですが、問題はコンパイル時に不正なオブジェクト/ msgの組み合わせを防ぐための型チェッカーが得られないことです。私は私の質問を言い換える必要があると思う、私はそれが本当に私が欲しいものではないことがわかります。 – martingw

関連する問題