2016-03-28 15 views
2

Kullanım durumu: Oyunun kurallarını her iki tarafta kodlayabilmem ve durum değişiklikleriyle iletişim kurabilmem için bir ghcjs ön ucunda ve temelde aynı durumu kullanan bir arka uçta bir oyun yazıyorum. . Bu amaçla, bir oyun sahası, her bir oyuncunun bizim tarafımızdan ve ussr tarafından temsil edildiği yerde bir bakıma benzeyecekti. Her oyuncunun bir eli var ve sunucu bakış açısından gayet yeterli ve her iki oyuncunun elinde her kartı biliyor. Ama bize, oyuncunun bakış açısından, gamestate buTürü aileler belirsiz değişken hatasına neden olur

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

gibi daha, kendi eli görebilirsiniz, ama sadece rakibi kaç kartları görebilirsiniz görünüyor. Bir gözlemci daha az görür. Oyunun kuralları karmaşıktır ve çok fazla şey olabilir, bu yüzden kuralları bir kez kodlamak harika olurdu, öyle ki yeni kartlar gibi oyuncunun elini etkileyen kurallar göstermek için bir oyuncuyu zorlamak gibi Bir türdeki kart, vs., her bir eli, kim olduklarına göre uygun şekilde etkiler. Bu amaçla, her iki hatlarında 75 üzerinde

{-# 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) 

hata çalışmak ve 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 

Veya eğer çizgisinde her ikisi şey vardır etmeyen, tip ailelerini kullanarak aşağıdaki yazma sona erdi Ben

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 

Ben benzer sitedeki bazı diğer yanıtları görmek bir tür deklarasyonu ihmal, ancak düzeltmeler sonuçta bir yöntemdir ben umut ediyorum cevap, yol açacağını açıkladı nasıl emin değilim t o serverToPlayerGS ve serverToPlayerEvent komutlarını yazım kurallarına göre yazıp kullanışlıdır.

cevap

5

sorun senin tipin aile injektif olmamasıdır: örneği a tam olarak ne olduğunu söylemez için USHand a bilerek Private şudur: USSR olabilir ancak her iki örneği beyan çünkü o da Observer olabilir:

type USHand USSR = Private 
type USHand Observer = Private 

toUS işlevi Public -> USHand a türünde olduğu için, a bir şekilde tahmin edilmeli ve bunun mümkün olmadığını gördük.

Bunu düzeltmek için proxy'leri tanıtmanız gerekecek. Eğer Haskell a tahmin etmek mümkün olmadığı için bir işlevi f :: F a varsa en belirtmek edebilmek için

data Proxy a = Proxy 

, sırayla bir f :: Proxy a -> F a içine açabilirsiniz: Proxy basit veri türü olarak tanımlanmaktadır a numaralı telefonu arayarak, a'un Int olmasını istediğiniz durumda f (Proxy :: Proxy Int) yazarak kastediyorum.

Kapsamlı tür değişkenlere gereksiniminiz olacaktır, çünkü toUs ile kullanacağınız, işlevinizin tür açıklamalarından gelecektir.Dolayısıyla bunu dosyasının en az bu iki satırı eklemek gerekir:

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

Ve ardından Public -> USHand a den toUS türünü değiştirmek:

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

bir kukla argüman _ eklemeyi unutmayın tüm örnek beyanlarınız toUs. Son olarak, serverToPlayerGS tanımınızı aşağıdaki gibi yapabilirsiniz:

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

Bu tamamen çalışır. Çok teşekkürler! Ayrıca, Proxy türünün ghc'nin gelecekteki bir sürümünün gerekli olmayabileceğini duydum. Bu konuda herhangi bir bilgi için herhangi bir referans var mı? –

+3

@mindreader, Evet 'TypeApplication' uzantısı budur. Mevcut bir tahliye adayına sahip olan GHC 8'e inecek. https://ghc.haskell.org/trac/ghc/wiki/TypeApplication – hao