2013-12-11 37 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

这张图在你需要它之​​前是有点吓人的 - 而且是正确的错字。这就是我没有实际类型检查自己的代码。 :) –