2013-12-11 73 views
9

我有以下代碼。我希望能夠在給定遊戲狀態時修改主動玩家的生活。我想出了一個activePlayer鏡頭,但當我嘗試,結合了-=操作我收到以下錯誤使用它:Haskell - 透鏡,使用'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`` 

和有問題的代碼:

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

每個玩家他們依次進行。我需要一次跟蹤所有的球員以及目前處於活躍狀態的球員,這就是我如何構建這一點的原因,儘管我對不同的結構開放,因爲我可能還沒有正確的結構。

+2

我認爲你的問題是'activePlayer'的定義允許它作爲一個getter,但不能作爲setter - 你已經告訴它如何將一名玩家拉出序列,但不知道如何改變主動玩家 - 因此它不能用來修改玩家。查看'''''''的類型:我將'輸出到'::(a - > c) - > Getter a b c d' –

回答

12

當您在鏡頭庫中編寫各種項目時,它們可能會根據某種子類型(參見下文)丟失相應的功能。在這種情況下,你已經由Lensplayers)與Getterto f一些功能f),因此組合僅僅是一個Getterover作用於鏡頭,既可以獲取和設置。

activePlayer應該形成一個有效的鏡頭,但是,所以你可以把它作爲一個getter/setter手動手動編寫。我在假設索引永遠不會失效的情況下部分寫下它。

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) } 

爲了更好地明白將要發生在lens庫中的子類型,我們可以使用the Big Lattice Diagram from Hackage

the Big Lattice Diagram from Hackage

每當你將兩個鏡頭類型與您圖表中結束了他們的第一個共同的後代(.) 。所以如果你結合LensPrism你可以看到他們的箭頭匯聚在Traversal。如果您合併LensGetter(其中to f是),那麼您將獲得Getter,因爲GetterLens的直接後代。

+0

謝謝,那張圖現在確實有意義。之前很可怕。一個可能的錯字,我認爲你的設置類型應該是'遊戲 - >玩家 - >遊戲'正確?至少,'Player - > Game - > Player'在我嘗試時沒有進行類型檢查。 – Dwilson

+0

這張圖在你需要它之​​前是有點嚇人的 - 而且是正確的錯字。這就是我沒有實際類型檢查自己的代碼。 :) –