1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980 |
- {-# LANGUAGE RankNTypes #-}
- {-# LANGUAGE LambdaCase #-}
- {-# LANGUAGE DeriveFunctor #-}
- -- Okay, I'm gonna justify this in a comment: I will never, under any
- -- circumstances, build a library that has an explicit `lens` dependency.
- -- I think `lens` is awesome, but it is also a giant package, and I
- -- don't want to inflict it on end-users who might not want it.
- -- I am more okay with lens-family-core, but in this case, all I need
- -- is really the `makeLens` function, which (not counting whitespace
- -- and comments) is three lines of code. Three lines! And it doesn't make
- -- sense to drag in a whole extra package when I can just copy this
- -- in.
- -- There's also reimplementations of `get` and `set` for possible internal
- -- use---three lines each, for a total of nine. Nine lines of
- -- easily-copyable, verifiable boilerplate. Instead of another dependency
- -- that must be downloaded and installed and managed by Cabal and
- -- addressed in constraint-solving...
- -- And that is why this module reimplement a few `lens` functions.
- module Codec.ActivityStream.LensInternal
- ( get
- , set
- , Lens'
- , makeLens
- , makeAesonLensMb
- , makeAesonLens
- ) where
- import Data.Aeson as Aeson
- import qualified Data.HashMap.Strict as HM
- import Data.Maybe (fromJust)
- import Data.Text (Text)
- -- We need these to write get and set
- newtype C a b = C { fromC :: a } deriving (Functor)
- newtype I a = I { fromI :: a } deriving (Functor)
- -- This is the same type alias as in @Control.Lens@, and so can be used
- -- anywhere lenses are needed.
- type Lens' a b = forall f. Functor f => (b -> f b) -> (a -> f a)
- get :: Lens' a b -> a -> b
- get lens a = fromC (lens C a)
- set :: Lens' a b -> b -> a -> a
- set lens x a = fromI (lens (const I x) a)
- makeLens :: (a -> b) -> (b -> a -> a) -> Lens' a b
- makeLens get set f a = (`set` a) `fmap` f (get a)
- -- This is necessary because of the way we store values as Aeson
- -- values underneath.
- fromJSON' :: FromJSON a => Aeson.Value -> Maybe a
- fromJSON' v = case fromJSON v of
- Success a -> Just a
- Error _ -> Nothing
- -- Create a lens into an Aeson object wrapper that takes and
- -- returns a Maybe value. When used as a setter, it can either
- -- insert a value in, or delete it from the object (if it is
- -- used with Nothing.)
- makeAesonLensMb :: (FromJSON v, ToJSON v)
- => Text -> Lens' c Aeson.Object -> Lens' c (Maybe v)
- makeAesonLensMb key fromObj = fromObj . makeLens g s
- where g o = HM.lookup key o >>= fromJSON'
- s (Just v) o = HM.insert key (toJSON v) o
- s Nothing o = HM.delete key o
- -- Create a lens into an Aeson object wrapper. This will fail if
- -- the object does not contain the relevant key.
- makeAesonLens :: (FromJSON v, ToJSON v)
- => Text -> Lens' c Aeson.Object -> Lens' c v
- makeAesonLens key fromObj = fromObj . makeLens g s
- where g o = fromJust (HM.lookup key o >>= fromJSON')
- s v o = HM.insert key (toJSON v) o
|