15

私は最近、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つのオブジェクトに含まれる可能性のあるすべてのタイプで意味的に無意味なモノイドインスタンスを必要としないかどうか疑問に思いました。マップに保存します。

それとも、ここで問題を完全に誤解していて、かなり大きなレンズパッケージのまったく異なるビットを使用する必要があるのでしょうか?

4

3 に答える 3

24

があり、最初の要素にTraversalaを取得したい場合は、の代わりにを使用できます。MaybeheadOfview

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

のインフィックスバージョンはheadOfと呼ばれ^?ます。toListOfまた、すべての要素のリストや、実行したい内容に応じたその他の関数を取得するために使用することもできます。Control.Lens.Foldドキュメントを参照してください。

関数を検索するモジュールのクイックヒューリスティック:

  • AGetterは、1つの値の読み取り専用ビューです
  • ALensは、正確に1つの値の読み取り/書き込みビューです。
  • ATraversalは、0個以上の値の読み取り/書き込みビューです。
  • AFoldは、0個以上の値の読み取り専用ビューです
  • ASetterは、0個以上の値(実際には数え切れないほど多くの値)の書き込み専用(まあ、変更専用)のビューです。
  • AnIsoは、まあ、同型写像です-Lensどちらの方向にも進むことができます
  • おそらく、関数を使用していることを知っているので、対応するモジュールIndexedを調べることができますIndexed

あなたがやろうとしていることと、それを入れるための最も一般的なモジュールが何であるかを考えてください。:-)この場合、あなたはを持ってTraversalいますが、あなたは表示しようとしているだけで、変更はしていないので、あなたが望む関数はにあり.Foldます。正確に1つの値を参照しているという保証もある場合は、になります.Getter

于 2012-11-18T01:14:45.517 に答える
1

簡単な答え:レンズパッケージは魔法ではありません。

エラーまたはデフォルトが何であるかを私に言わずに、あなたは作りたいです:

viewPlayerLocation :: World-> PlayerId-> RoomId

あなたは2つのことを知っています、それ

レンズ付きのものを回収するには、atレンズから返されるかもしれないものを処理する必要があります

最終結果がMonoidのインスタンスである限り、タイプチェックを行うトラバース

を使用すると、ルックアップが失敗したときのデフォルトとしてMonoid取得します。mempty :: Monoid m => m

失敗する可能性のあるもの:はに含めるPlayerIdことはできず、はに含めること_worldPlayers_playerLocationできません_worldRooms

では、ルックアップが失敗した場合、コードは何をすべきでしょうか?これは「不可能」ですか?その場合は、を使用fromMaybe (error "impossible") :: Maybe a -> aしてクラッシュします。

ルックアップが失敗する可能性がある場合、適切なデフォルトはありますか?おそらく戻っMaybe RoomIdて、発信者に決定させますか?

于 2012-11-17T22:58:01.550 に答える
1

^?!を呼び出すことからあなたを解放するものがありますfromMaybe

于 2014-12-03T00:20:21.020 に答える