2016-03-28 14 views
2

使用例:ghcjsフロントエンドとバックエンドに本質的に同じ状態を使用するゲームを作成しているため、ゲームのルールを両方向でエンコードして状態の変化と通信できる。そのためには、gamestateは次のようになりますタイプファミリではあいまいな変数エラーが発生する

data GameState = GameState { 
    gameTurn :: Int,   -- | Everyone sees 
    gamePhase :: GamePhase,  -- | this 
    boardState :: BoardState, -- | stuff 
    -- a lot more stuff everyone can see, followed by 
    usHand :: [Card], 
    ussrHand :: [Card] 
} 

各プレイヤーはusとussrで表されます。各プレイヤーは手を持ち、サーバーの観点からは無能であり、両方のプレイヤーのすべてのカードを知っています。しかし、私たちのプレイヤーの視点から、gamestateは、より多くのです、彼は彼自身の手を見ることができますが、彼は彼の相手が持っているどのように多くのカードを参照することができ、この

data GameState = GameState { 
    -- public stuff 
    usHand :: [Card], 
    ussrHand :: Int 
} 

のように見えます。観察者はそれほど目に見えない。ゲームのルールは複雑であり、起こりうることがたくさんあるので、ルールを一度エンコードすることは素晴らしいでしょう。たとえば、新しいカードを扱うなどのプレーヤーの手に影響を及ぼすルール、プレイヤーにタイプのカードなどは、彼らが誰であるかに応じて適切な方法で各手に影響を及ぼします。そのために、私はライン75と78の両方に

{-# LANGUAGE TypeFamilies, RankNTypes #-} 
module Test where 

data Card = Card 
data BoardState = BoardState 
data GamePhase = GamePhase 
data Country 
data Player = PUS | PUSSR 

data US 
data USSR 
data Observer 
data Server 

data Private = Private Int 
data Public = Public [Card] 

class HandType a where 
    type USHand a :: * 
    type USSRHand a :: * 
    toUS :: Public -> USHand a 
-- toUSSR :: Public -> USSRHand a -- TODO 

instance HandType Server where 
    type USHand Server = Public 
    type USSRHand Server = Public 
    toUS (Public cs) = Public cs 

instance HandType US where 
    type USHand US = Public 
    type USSRHand US = Private 
    toUS (Public cs) = Public cs 

instance HandType USSR where 
    type USHand USSR = Private 
    type USSRHand USSR = Public 
    toUS (Public cs) = Private (length cs) 

instance HandType Observer where 
    type USHand Observer = Private 
    type USSRHand Observer = Private 
    toUS (Public cs) = Private (length cs) 

data GameState a = GameState { 
    gameTurn :: Int,   -- | Everyone sees 
    gamePhase :: GamePhase,  -- | this 
    boardState :: BoardState, -- | stuff 

    usHand :: USHand a, 
    ussrHand :: USSRHand a 
} 

data Event a = 
    PlaceInfluence Player Int Country -- | Most plays don't affect 
    | PlayCard Player Card    -- | either hand 
    | DealCards (USHand a) (USSRHand a) -- | This one does 

-- Works 
obsEvents :: [Event US] 
obsEvents = [PlayCard PUS Card, PlayCard PUSSR Card, DealCards (Public [Card]) (Private 3)] 

-- Works 
serverEvents :: [Event Server] 
serverEvents = [PlayCard PUS Card, PlayCard PUSSR Card, DealCards (Public [Card, Card]) (Public [Card])] 

-- The server must send out the gamestate modified for the player's consumption. 
-- serverToPlayerGS :: GameState Server -> GameState a 
serverToPlayerGS (GameState turn phase bs us ussr) = 
    GameState turn phase bs (toUS us) undefined -- | <- Doesn't work (line 75) 

-- serverToPlayerEvent :: Event Server -> Event a 
serverToPlayerEvent (PlaceInfluence p amt c) = PlaceInfluence p amt c 
serverToPlayerEvent (PlayCard p c) = PlayCard p c 
serverToPlayerEvent (DealCards us ussr) = 
    DealCards (toUS us) undefined -- | <- Doesn't work (line 78) 

エラーが動作しないタイプファミリを使用して、次のことを書いてしまったの両方が

Couldn't match expected type ‘USHand a’ 
      with actual type ‘USHand a0’ 
NB: ‘USHand’ is a type function, and may not be injective 
The type variable ‘a0’ is ambiguous 
Relevant bindings include 
    serverToPlayerGS :: GameState Server -> GameState a 
    (bound at src/Test4.hs:74:1) 
In the fourth argument of ‘GameState’, namely ‘(toUS us)’ 
In the expression: GameState turn phase bs (toUS us) undefined 

場合や、の線に沿って何かあります私は似ているサイト上のいくつかの他の回答を参照してください、私は修正が説明かどうかはわかりません最終的な方法である私が望んでいる答えは、につながる型宣言

Could not deduce (USHand a0 ~ USHand a1) 
from the context (HandType a1, 
        USHand a1 ~ USHand a, 
        USHand t ~ Public) 
    bound by the inferred type for ‘serverToPlayerGS’: 
      (HandType a1, USHand a1 ~ USHand a, USHand t ~ Public) => 
      GameState t -> GameState a 
    at src/Test4.hs:(74,1)-(75,45) 
NB: ‘USHand’ is a type function, and may not be injective 
The type variable ‘a0’ is ambiguous 
Expected type: USHand a 
    Actual type: USHand a0 
When checking that ‘serverToPlayerGS’ has the inferred type 
    serverToPlayerGS :: forall t a a1. 
         (HandType a1, USHand a1 ~ USHand a, USHand t ~ Public) => 
         GameState t -> GameState a 
Probable cause: the inferred type is ambiguous 

を省略しますt o型チェックと有用な方法で、serverToPlayerGSとserverToPlayerEventを記述します。

答えて

5

問題はあなたのタイプの家族は単射ではないということです:USHand aは、例えばPrivateであることを知ることは、まさにaがあるあなたに教えてくれない:それはUSSR可能性がありますが、両方のインスタンスが宣言しているため、それはまたObserver次のようになります。

type USHand USSR = Private 
type USHand Observer = Private 

機能toUSはタイプPublic -> USHand aであるため、aは何とか推測する必要があり、私たちはそれが不可能であることを見ました。

これを修正するには、プロキシを導入する必要があります。あなたはHaskellはaを推測することはできませんされている機能f :: F aを持っている場合、あなたはで指定できるようにするために、f :: Proxy a -> F aにそれを回すことができ

data Proxy a = Proxy 

:プロキシは以下のように定義、単純なデータ型でありますあなたがaIntにしたい場合には、をf (Proxy :: Proxy Int)と書くことを意味します。

toUsで使用するaは、関数の型名に由来するため、スコープ付きの型変数が必要です。つまり、あなたのファイルの先頭に次の2行を追加する必要があります

{-# LANGUAGE ScopedTypeVariables #-} 
import Data.Proxy 

そしてにPublic -> USHand aからtoUSの種類を変更:

toUS :: Proxy a -> Public -> USHand a 

はダミー引数_を追加することを忘れないでください。すべてのインスタンス宣言はtoUsです。最後に、serverToPlayerGSの定義を次のようにパッチすることができます。

serverToPlayerGS :: forall a. HandType a => GameState Server -> GameState a 
serverToPlayerGS (GameState turn phase bs us ussr) = 
    GameState turn phase bs (toUS (Proxy :: Proxy a) us) undefined 
+0

これは完全に機能します。どうもありがとう!また、ghcの将来のリリースでは、Proxyタイプが必要ないかもしれないと聞きました。誰もそれに関する情報への参照を持っていますか? –

+3

@mindreader、はい、それは 'TypeApplication'拡張です。 GHC 8に着陸します.GHC 8には既にリリース候補があります。 https://ghc.haskell.org/trac/ghc/wiki/TypeApplication – hao

関連する問題