How do I handle the Maybe result of at in Control.Lens.Indexed without a Monoid instance

倖福魔咒の 提交于 2019-12-04 23:18:33

If you have a Traversal and you want to get a Maybe for the first element, you can just use headOf instead of view, i.e.

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

The infix version of headOf is called ^?. You can also use toListOf to get a list of all elements, and other functions depending on what you want to do. See the Control.Lens.Fold documentation.

A quick heuristic for which module to look for your functions in:

  • A Getter is a read-only view of exactly one value
  • A Lens is a read-write view of exactly one value
  • A Traversal is a read-write view of zero-or-more values
  • A Fold is a read-only view of zero-or-more values
  • A Setter is a write-only (well, modify-only) view of zero-or-more values (possibly uncountably many values, in fact)
  • An Iso is, well, an isomorphism -- a Lens that can go in either direction
  • Presumably you know when you're using an Indexed function, so you can look in the corresponding Indexed module

Think about what you're trying to do and what the most general module to put it in would be. :-) In this case you have a Traversal, but you're only trying to view, not modify, so the function you want is in .Fold. If you also had the guarantee that it was referring to exactly one value, it would be in .Getter.

Short answer: the lens package is not magic.

Without telling me what the error or default is, you want to make:

viewPlayerLocation :: World -> PlayerId -> RoomId

You know two things, that

To retrieve those with lenses I need to handle the Maybe returned by the at lens

and

traverse which does typecheck as long as the final result is an instance of Monoid

With a Monoid you get mempty :: Monoid m => m as the default when the lookup fails.

What can fail: The PlayerId can not be in the _worldPlayers and the _playerLocation can not be in the _worldRooms.

So what should your code do if a lookup fails? Is this "impossible" ? If so, then use fromMaybe (error "impossible") :: Maybe a -> a to crash.

If it possible for the lookup to fail then is there a sane default? Perhaps return Maybe RoomId and let the caller decide?

There is ^?! which frees you from calling fromMaybe.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!