LensInternal.hs 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. {-# LANGUAGE RankNTypes #-}
  2. {-# LANGUAGE LambdaCase #-}
  3. {-# LANGUAGE DeriveFunctor #-}
  4. -- Okay, I'm gonna justify this in a comment: I will never, under any
  5. -- circumstances, build a library that has an explicit `lens` dependency.
  6. -- I think `lens` is awesome, but it is also a giant package, and I
  7. -- don't want to inflict it on end-users who might not want it.
  8. -- I am more okay with lens-family-core, but in this case, all I need
  9. -- is really the `makeLens` function, which (not counting whitespace
  10. -- and comments) is three lines of code. Three lines! And it doesn't make
  11. -- sense to drag in a whole extra package when I can just copy this
  12. -- in.
  13. -- There's also reimplementations of `get` and `set` for possible internal
  14. -- use---three lines each, for a total of nine. Nine lines of
  15. -- easily-copyable, verifiable boilerplate. Instead of another dependency
  16. -- that must be downloaded and installed and managed by Cabal and
  17. -- addressed in constraint-solving...
  18. -- And that is why this module reimplement a few `lens` functions.
  19. module Codec.ActivityStream.LensInternal
  20. ( get
  21. , set
  22. , Lens'
  23. , makeLens
  24. , makeAesonLensMb
  25. , makeAesonLens
  26. ) where
  27. import Data.Aeson as Aeson
  28. import qualified Data.HashMap.Strict as HM
  29. import Data.Maybe (fromJust)
  30. import Data.Text (Text)
  31. -- We need these to write get and set
  32. newtype C a b = C { fromC :: a } deriving (Functor)
  33. newtype I a = I { fromI :: a } deriving (Functor)
  34. -- This is the same type alias as in @Control.Lens@, and so can be used
  35. -- anywhere lenses are needed.
  36. type Lens' a b = forall f. Functor f => (b -> f b) -> (a -> f a)
  37. get :: Lens' a b -> a -> b
  38. get lens a = fromC (lens C a)
  39. set :: Lens' a b -> b -> a -> a
  40. set lens x a = fromI (lens (const I x) a)
  41. makeLens :: (a -> b) -> (b -> a -> a) -> Lens' a b
  42. makeLens get set f a = (`set` a) `fmap` f (get a)
  43. -- This is necessary because of the way we store values as Aeson
  44. -- values underneath.
  45. fromJSON' :: FromJSON a => Aeson.Value -> Maybe a
  46. fromJSON' v = case fromJSON v of
  47. Success a -> Just a
  48. Error _ -> Nothing
  49. -- Create a lens into an Aeson object wrapper that takes and
  50. -- returns a Maybe value. When used as a setter, it can either
  51. -- insert a value in, or delete it from the object (if it is
  52. -- used with Nothing.)
  53. makeAesonLensMb :: (FromJSON v, ToJSON v)
  54. => Text -> Lens' c Aeson.Object -> Lens' c (Maybe v)
  55. makeAesonLensMb key fromObj = fromObj . makeLens g s
  56. where g o = HM.lookup key o >>= fromJSON'
  57. s (Just v) o = HM.insert key (toJSON v) o
  58. s Nothing o = HM.delete key o
  59. -- Create a lens into an Aeson object wrapper. This will fail if
  60. -- the object does not contain the relevant key.
  61. makeAesonLens :: (FromJSON v, ToJSON v)
  62. => Text -> Lens' c Aeson.Object -> Lens' c v
  63. makeAesonLens key fromObj = fromObj . makeLens g s
  64. where g o = fromJust (HM.lookup key o >>= fromJSON')
  65. s v o = HM.insert key (toJSON v) o