2012-11-17 55 views
15

我最近在Hackage上發現了鏡頭包,並且一直試圖在一個小型測試項目中使用它,可能會變成如果我繼續努力,MUD/MUSH服務器就是一個非常遙遠的日子。我該如何處理Control.Lens.Indexed中沒有Monoid實例的at的Maybe結果

這裏是我的代碼說明我面對現在與在

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

用於訪問鍵/值容器(Data.Map.Strict在我的情況)鏡片由於房間的問題最小化版本,玩家和類似的對象在我將它們存儲在我的世界狀態類型中的代碼中作爲Ids(新類型的UUID)映射到它們的數據對象。

爲了檢索那些鏡頭,我需要處理由鏡頭返回的Maybe(萬一鑰匙不在地圖中,這是Nothing)。在我的最後一行中,我試圖通過遍歷來做到這一點,只要最終結果是Monoid的一個實例,但是通常情況並非如此。在這裏,它不是因爲playerLocation返回一個沒有Monoid實例的RoomId。

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' 

由於含半幺羣是由僅僅是因爲橫向推廣到規模大於一,我現在不知道是否有更好的方式來處理這個問題,不要求對所有類型的語義無意義含半幺羣實例可能是容器遍歷需要包含在我想要存儲在地圖中的一個對象中。

或者我可能完全誤解了這個問題,我需要使用完全不同的相當大的鏡頭包?

+0

如何使用'Data.Monoid'中的'First'或'Last' monoids? –

回答

21

如果你有Traversal,你想獲得一個Maybe爲第一要素,你可以只使用headOf代替view,即

viewPlayerLocation :: World -> PlayerId -> Maybe RoomId 
viewPlayerLocation world playerId = 
    headOf (worldPlayers.at playerId.traverse.playerLocation) world 

headOf中綴版本被稱爲^?。您還可以使用toListOf來獲取所有元素的列表以及其他功能,具體取決於您想要執行的操作。請參閱Control.Lens.Fold文檔。

快速啓發式哪個模塊爲你尋找功能:

  • 一個Getter是一個值
  • 一個Lens的只讀視圖是一個值的讀寫視圖
  • Traversal是零或更多的值
  • Fold的讀寫視圖零或更多的值的只讀視圖
  • A Setter是零或更多值(實際上可能是不計數的許多值)的只寫(完好修改)視圖
  • Iso是一個同構 - 一個Lens可以去在任一方向
  • 想必你知道,當你使用一個Indexed功能,讓你可以看看相應Indexed模塊

想想你正在試圖做什麼,什麼最普遍的模塊把它會在。 :-)在這種情況下,你有一個Traversal,但你只是想查看,而不是修改,所以你想要的功能是.Fold。如果您也有保證它指的只是一個值,則它將在.Getter中。

+0

謝謝,headOf似乎提供了我正在尋找的東西。是的,找出哪個模塊可以容納我需要的功能是很棘手的。你的啓發式會派上用場。 –

1

簡答:鏡頭包並不神奇。

沒有告訴我什麼是錯誤的或默認的是,你想:

viewPlayerLocation ::世界 - > PlayerId - > RoomId

你知道兩兩件事,即

要檢索那些鏡頭,我需要處理由鏡頭返回的可能性

移,只要其不作爲類型檢查的最終結果是含半幺羣

的實例

隨着Monoidmempty :: Monoid m => m作爲默認當查找失敗。

什麼可以失敗:PlayerId不能在_worldPlayers_playerLocation不能在_worldRooms

那麼如果查找失敗,你的代碼應該怎麼做?這是「不可能」嗎?如果是,則使用fromMaybe (error "impossible") :: Maybe a -> a來崩潰。

如果可能的查找失敗,那麼是否有一個理智的默認值?也許返回Maybe RoomId並讓來電者決定?

+0

是的,我應該提到的是,如果可能的話,我希望它返回一個Maybe RoomId,並向外傳播Maybe值。對於二傳手來說,如果沒有匹配的ID的地圖條目不存在,但是可以不做任何事情,但是某種錯誤報告將是首選。 –

1

^?!它可以讓你免費撥打fromMaybe

相關問題