2013-12-11 18 views
9

Tôi có mã sau đây. Tôi muốn có thể thay đổi cuộc sống của người chơi đang hoạt động khi đưa ra trạng thái trò chơi. Tôi đã đưa ra một ống kính activePlayer, nhưng khi tôi cố gắng và sử dụng nó kết hợp với -= điều hành tôi nhận được lỗi sau:Haskell - Ống kính, sử dụng chức năng 'to'

> over (activePlayer.life) (+1) initialState 
<interactive>:2:7: 
    No instance for (Contravariant Mutator) 
     arising from a use of `activePlayer' 
    Possible fix: 
     add an instance declaration for (Contravariant Mutator) 
    In the first argument of `(.)', namely `activePlayer' 
    In the first argument of `over', namely `(activePlayer . life)' 
    In the expression: over (activePlayer . life) (+ 1) initialState`` 

và mã trong câu hỏi:

{-# LANGUAGE TemplateHaskell #-} 
module Scratch where 

import Control.Lens 
import Control.Monad.Trans.Class 
import Control.Monad.Trans.State 
import Data.Sequence (Seq) 
import qualified Data.Sequence as S 

data Game = Game 
    { _players :: (Int, Seq Player) -- active player, list of players 
    , _winners :: Seq Player 
    } 
    deriving (Show) 

initialState = Game 
    { _players = (0, S.fromList [player1, player2]) 
    , _winners = S.empty 
    } 

data Player = Player 
    { _life :: Integer 
    } 
    deriving (Show, Eq) 

player1 = Player 
    { _life = 10 
    } 

player2 = Player 
    { _life = 10 
    } 

makeLenses ''Game 
makeLenses ''Player 

activePlayer 
    :: (Functor f, Contravariant f) => 
     (Player -> f Player) -> Game -> f Game 
activePlayer = players.to (\(i, ps) -> S.index ps i) 

Mỗi người chơi mất đến lượt họ. Tôi cần phải theo dõi tất cả các cầu thủ cùng một lúc cũng như hiện đang hoạt động, đó là lý do tôi cấu trúc như thế nào, mặc dù tôi mở ra các cấu trúc khác nhau vì tôi có thể chưa có cấu trúc phù hợp.

+2

Tôi nghĩ rằng vấn đề của bạn là định nghĩa 'activePlayer' cho phép nó hoạt động như một trình thu thập, nhưng không phải là người đặt cược - bạn đã nói với nó cách kéo một người chơi ra khỏi chuỗi, nhưng không phải cách thay đổi người chơi hoạt động - do đó nó không thể được sử dụng để sửa đổi người chơi. Kiểm tra kiểu 'to' ---'>: i to' kết quả trong 'to :: (a -> c) -> Getter a b c d' –

Trả lời

12

Khi bạn soạn nhiều mục khác nhau trong thư viện ống kính với (.), chúng có thể mất khả năng theo loại kiểu phụ (xem bên dưới). Trong trường hợp này, bạn đã sáng tác một Lens (players) với Getter (to f cho một số chức năng f) và do đó kết hợp chỉ là Getter trong khi over hoạt động trên các ống kính mà cả hai có thể nhận và đặt. Tuy nhiên,

activePlayer nên tạo thành một ống kính hợp lệ để bạn có thể viết thủ công như một cặp getter/setter. Tôi đang viết nó một phần dưới đây dưới giả định rằng chỉ mục không bao giờ có thể không hợp lệ.

activePlayer :: Lens' Game Player 
activePlayer = lens get set 
    where 
    get :: Game -> Player 
    get (Game { _players = (index, seq) }) = Seq.index seq index 

    set :: Game -> Player -> Game 
    set [email protected](Game { _players = (index, seq) }) player = 
     g { _players = (index, Seq.update index player seq) } 

Để hiểu rõ hơn về subtyping đó xảy ra trong thư viện lens chúng ta có thể sử dụng the Big Lattice Diagram from Hackage

the Big Lattice Diagram from Hackage

Bất cứ khi nào bạn kết hợp hai loại ống kính với (.) bạn kết thúc với hậu duệ chung đầu tiên của họ trong bảng xếp hạng mà . Vì vậy, nếu bạn kết hợp LensPrism, bạn có thể thấy rằng mũi tên của chúng hội tụ trên Traversal. Nếu bạn kết hợp LensGetter (trong đó to f là) thì bạn sẽ nhận được Getter từ Getter là hậu duệ trực tiếp của Lens.

+0

Cảm ơn, sơ đồ đó thực sự có ý nghĩa bây giờ. Đã đáng sợ trước đây. Một typo có thể xảy ra, tôi nghĩ kiểu tập hợp của bạn phải là 'Trò chơi -> Người chơi -> Trò chơi' chính xác? Ít nhất, 'Người chơi -> Trò chơi -> Người chơi' không đánh dấu kiểm tra khi tôi thử. – Dwilson

+0

Sơ đồ đó rất đáng sợ cho đến khi bạn cần - và yup, chính xác là lỗi chính tả. Đó là những gì tôi nhận được không thực sự gõ-kiểm tra mã của riêng tôi. :) –

Các vấn đề liên quan