Recentemente ho scoperto il pacchetto di obiettivi su Hackage e ho cercato di utilizzarlo ora in un piccolo progetto di test che potrebbe trasformarsi in un MUD/MUSH server un giorno molto lontano se continuo a lavorarci.Come gestisco il risultato Maybe di in Control.Lens.Indexed senza un'istanza Monoid
Ecco una versione ridotta a icona del mio codice che illustra il problema che sto affrontando in questo momento con la a lenti utilizzate per accedere ai contenitori chiave/valore (Data.Map.Strict nel mio caso)
{-# LANGUAGE OverloadedStrings, GeneralizedNewtypeDeriving, TemplateHaskell #-}
module World where
import Control.Applicative ((<$>),(<*>), pure)
import Control.Lens
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as DM
import Data.Maybe
import Data.UUID
import Data.Text (Text)
import qualified Data.Text as T
import System.Random (Random, randomIO)
newtype RoomId = RoomId UUID deriving (Eq, Ord, Show, Read, Random)
newtype PlayerId = PlayerId UUID deriving (Eq, Ord, Show, Read, Random)
data Room =
Room { _roomId :: RoomId
, _roomName :: Text
, _roomDescription :: Text
, _roomPlayers :: [PlayerId]
} deriving (Eq, Ord, Show, Read)
makeLenses ''Room
data Player =
Player { _playerId :: PlayerId
, _playerDisplayName :: Text
, _playerLocation :: RoomId
} deriving (Eq, Ord, Show, Read)
makeLenses ''Player
data World =
World { _worldRooms :: Map RoomId Room
, _worldPlayers :: Map PlayerId Player
} deriving (Eq, Ord, Show, Read)
makeLenses ''World
mkWorld :: IO World
mkWorld = do
r1 <- Room <$> randomIO <*> (pure "The Singularity") <*> (pure "You are standing in the only place in the whole world") <*> (pure [])
p1 <- Player <$> randomIO <*> (pure "testplayer1") <*> (pure $ r1^.roomId)
let rooms = at (r1^.roomId) ?~ (set roomPlayers [p1^.playerId] r1) $ DM.empty
players = at (p1^.playerId) ?~ p1 $ DM.empty in do
return $ World rooms players
viewPlayerLocation :: World -> PlayerId -> RoomId
viewPlayerLocation world playerId=
view (worldPlayers.at playerId.traverse.playerLocation) world
Dal camere , i giocatori e gli oggetti simili sono referenziati su tutto il codice che li memorizzo nel mio stato di stato World come mappe di Id (UUID newtyped) ai loro oggetti di dati.
Per recuperare quelli con obiettivi, devo gestire il valore restituito dall'obiettivo (nel caso in cui la chiave non sia nella mappa questo è Nothing) in qualche modo. Nella mia ultima riga ho provato a farlo attraverso la traversa che fa typecheck fintanto che il risultato finale è un'istanza di Monoid ma questo non è generalmente il caso. Proprio qui non è perché playerLocation restituisce un RoomId che non ha un'istanza Monoid.
No instance for (Data.Monoid.Monoid RoomId)
arising from a use of `traverse'
Possible fix:
add an instance declaration for (Data.Monoid.Monoid RoomId)
In the first argument of `(.)', namely `traverse'
In the second argument of `(.)', namely `traverse . playerLocation'
In the second argument of `(.)', namely
`at playerId . traverse . playerLocation'
Poiché la Monoide è richiesto da traverse solo perché traslazione generalizza a contenitori di dimensioni maggiori di uno ora mi chiedevo se esiste un modo migliore per gestire questa che non richiede istanze Monoide semanticamente senza senso su tutti i tipi possibilmente contenuto in uno dei miei oggetti che voglio memorizzare nella mappa.
O forse ho frainteso completamente il problema qui e ho bisogno di utilizzare un bit completamente diverso del pacchetto di lenti piuttosto grande?
Che dire dell'utilizzo dei 'mono' o' ultimo' da 'Data.Monoid'? –