瀏覽代碼

Modified external interface of LensInternal module

Getty Ritter 9 年之前
父節點
當前提交
a497ef2677
共有 1 個文件被更改,包括 48 次插入17 次删除
  1. 48 17
      Codec/ActivityStream/LensInternal.hs

+ 48 - 17
Codec/ActivityStream/LensInternal.hs

@@ -1,54 +1,80 @@
 {-# LANGUAGE RankNTypes #-}
 {-# LANGUAGE RankNTypes #-}
 {-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE DeriveFunctor #-}
 
 
-module Codec.ActivityStream.LensInternal where
+-- 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           Data.Aeson as Aeson
 import qualified Data.HashMap.Strict as HM
 import qualified Data.HashMap.Strict as HM
 import           Data.Maybe (fromJust)
 import           Data.Maybe (fromJust)
 import           Data.Text (Text)
 import           Data.Text (Text)
 
 
-newtype Const a b = Const { fromConst :: a }
-instance Functor (Const a) where fmap f (Const x) = Const x
-
 -- We need these to write get and set
 -- We need these to write get and set
-newtype Id a = Id { fromId :: a }
-instance Functor Id where fmap f (Id x) = Id (f x)
+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)
 type Lens' a b = forall f. Functor f => (b -> f b) -> (a -> f a)
 
 
 get :: Lens' a b -> a -> b
 get :: Lens' a b -> a -> b
-get lens a = fromConst (lens Const a)
+get lens a = fromC (lens C a)
 
 
 set :: Lens' a b -> b -> a -> a
 set :: Lens' a b -> b -> a -> a
-set lens x a = fromId (lens (const Id x) a)
+set lens x a = fromI (lens (const I x) a)
 
 
 makeLens :: (a -> b) -> (b -> a -> a) -> Lens' a b
 makeLens :: (a -> b) -> (b -> a -> a) -> Lens' a b
 makeLens get set f a = (`set` a) `fmap` f (get a)
 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' :: FromJSON a => Aeson.Value -> Maybe a
 fromJSON' v = case fromJSON v of
 fromJSON' v = case fromJSON v of
   Success a -> Just a
   Success a -> Just a
   Error _   -> Nothing
   Error _   -> Nothing
 
 
 -- Create a lens into an Aeson object wrapper that takes and
 -- 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)
 makeAesonLensMb :: (FromJSON v, ToJSON v)
                 => Text -> Lens' c Aeson.Object -> Lens' c (Maybe v)
                 => Text -> Lens' c Aeson.Object -> Lens' c (Maybe v)
-makeAesonLensMb key fromObj = fromObj . lens
-  where lens = makeLens
-                 (\ o -> HM.lookup key o >>= fromJSON')
-                 (\case Just v  -> HM.insert key (toJSON v)
-                        Nothing -> HM.delete key)
+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)
 makeAesonLens :: (FromJSON v, ToJSON v)
               => Text -> Lens' c Aeson.Object -> Lens' c v
               => Text -> Lens' c Aeson.Object -> Lens' c v
-makeAesonLens key fromObj = fromObj . lens
-  where lens = makeLens
-                 (\ o -> fromJust (HM.lookup key o >>= fromJSON'))
-                 (\ v o -> HM.insert key (toJSON v) o)
+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