我该如何处理可能的在Control.Lens.Indexed导致没有一个Monoid实例(How d

2019-08-02 20:38发布

我最近发现了Hackage镜头包,一直试图利用它现在在一家小的测试项目,如果我坚持工作,可能变成一个MUD /烂糊服务器一个非常遥远的一天。

这里是我的代码出在用于访问键/值容器的镜头,我面对现在与问题最小化版本(Data.Map.Strict在我的情况)

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

由于房间,球员和相似的对象引用遍我把它们存储在我的世界状态类型的IDS(newtyped的UUID)映射到其数据对象的代码。

要检索那些镜头我需要处理也许通过在镜头莫名其妙返回(当钥匙不在地图上,这是没有)。 在我的上线我试图通过遍历只要最后的结果是含半幺群的实例,但是这不是一般的情况下,其不会进行类型检查,以做到这一点。 就在这里是不是因为playerLocation返回它没有含半幺群实例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'

由于含半幺群是通过遍历只是因为横向推广到规模大于一,我现在不知道是否有更好的方式来处理这个问题,不要求对可能包含在一个我的对象我想所有类型的语义上无意义的含半幺群实例的容器要求存储在地图中。

或者,也许我在这里完全误解的问题,我需要用相当大的透镜组件的一个完全不同的位?

Answer 1:

如果你有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是零或更多的值的只读视图
  • Setter是零或更多的值的一个只写(当然,修改-只)视图(可能不可数许多值,实际上)
  • 一个Iso ,没错,是同构的-一个Lens ,可以在任何一个方向走
  • 想必你知道,当你使用Indexed功能,这样你就可以在相应的查找Indexed模块

想想你想要做什么,什么最普遍的模块,把它放在会。 :-)在这种情况下你有Traversal ,但你只是想查看,不能修改,所以你想要的功能是.Fold 。 如果您也有它指的是一个值的保证,这将是.Getter



Answer 2:

答案很简单:镜头包不是魔术。

没有告诉我什么错误或过失,你想:

viewPlayerLocation ::世界 - > PlayerId - > RoomId

你知道两两件事,即

要检索那些镜头,我需要通过在镜头处理可能返回

横动,其只要最终结果是含半幺群的一个实例并不类型检查

随着Monoidmempty :: Monoid m => m作为默认时,查找失败。

什么会失败:该PlayerId不能在_worldPlayers_playerLocation不能在_worldRooms

那么你应该做的代码,如果查找失败? 这是“不可能的”? 如果是这样,那么使用fromMaybe (error "impossible") :: Maybe a -> a崩溃。

如果有可能的查找失败,那么有没有一个健全的默认? 也许返回Maybe RoomId ,让来电者决定?



Answer 3:

^?! 这样可以让您打电话fromMaybe



文章来源: How do I handle the Maybe result of at in Control.Lens.Indexed without a Monoid instance