私は最近、Hackageでレンズパッケージを発見し、それを小さなテストプロジェクトで利用しようとしています。このプロジェクトで作業を続けると、非常に遠い日にMUD/MUSHサーバーになる可能性があります。
これは、キー/値コンテナー(私の場合はData.Map.Strict)にアクセスするために使用されるatレンズで現在直面している問題を示すコードの最小化バージョンです。
{-# 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
部屋、プレーヤー、および同様のオブジェクトはコード全体で参照されるため、ID(newtyped UUID)のデータオブジェクトへのマップとしてWorld状態タイプでそれらを格納します。
レンズを持っている人を検索するには、atレンズから返された多分(キーがマップにない場合、これは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'
モノイドはトラバースによって必要とされるのは、トラバースが1より大きいサイズのコンテナーに一般化するためだけなので、これを処理するためのより良い方法があり、必要な1つのオブジェクトに含まれる可能性のあるすべてのタイプで意味的に無意味なモノイドインスタンスを必要としないかどうか疑問に思いました。マップに保存します。
それとも、ここで問題を完全に誤解していて、かなり大きなレンズパッケージのまったく異なるビットを使用する必要があるのでしょうか?