{-# LANGUAGE CPP #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveFoldable #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies #-} -- | A @Map@-like structure that contains up to one key-value pair -- -- A 'Meep' is strict in the key. -- -- @Meep k a@ is isomorphic to @Maybe (k, a)@ with 'maybeing' witnessing the isomorphism module Data.Meep #ifdef TEST ( Meep(..) #else ( Meep #endif , empty , singleton , size , null , fromMaybe , toMaybe , maybeing , keys , elems ) where import Control.Applicative (pure) import Control.Lens import Data.Monoid (mempty) import Data.Data (Data, Typeable) import Data.Foldable (Foldable) import Data.Semigroup (Semigroup(..)) import GHC.Generics (Generic) import Prelude hiding (null, lookup) #ifdef TEST import Test.QuickCheck (Arbitrary(..)) #endif {-# ANN module "HLint: ignore Use fromMaybe" #-} -- | A Meep from key @k@ to value @a@ data Meep k a = Empty | Meep !k a deriving (Eq, Ord, Functor, Foldable, Traversable, Typeable, Data, Generic) instance (Show k, Show a) => Show (Meep k a) where showsPrec p m = showParen (p > 10) (showString "fromMaybe " . shows (toMaybe m)) -- | 'Meep's intersection instance (Eq k, Semigroup a) => Semigroup (Meep k a) where Empty <> _ = Empty _ <> Empty = Empty Meep k v <> Meep k' v' = bool Empty (Meep k (v <> v')) (k == k') instance Eq k => Ixed (Meep k a) where ix = ixAt instance Eq k => At (Meep k a) where at k f m = indexed f k mv <&> \r -> case r of Nothing -> maybe m (const (delete k m)) mv Just v -> insert k v m where mv = lookup k m type instance Index (Meep k a) = k type instance IxValue (Meep k a) = a instance FunctorWithIndex k (Meep k) where imap _ Empty = Empty imap f (Meep k a) = Meep k (f k a) instance FoldableWithIndex k (Meep k) where ifoldMap _ Empty = mempty ifoldMap f (Meep k a) = f k a instance TraversableWithIndex k (Meep k) where itraverse _ Empty = pure Empty itraverse f (Meep k a) = fmap (Meep k) (f k a) instance AsEmpty (Meep k a) where _Empty = prism' (const Empty) (\x -> case x of Empty -> Just (); _ -> Nothing) #ifdef TEST instance (Arbitrary k, Arbitrary a) => Arbitrary (Meep k a) where arbitrary = fmap fromMaybe arbitrary #endif -- | /O(1)/. An empty 'Meep' empty :: Meep k a empty = Empty -- | /O(1)/. A 'Meep' with a single key-value pair singleton :: k -> a -> Meep k a singleton = Meep -- | /O(1)/. The size of the 'Meep' -- -- >>> size empty -- 0 -- -- >>> size (singleton 4 "foo") -- 1 size :: Num b => Meep k a -> b size = bool 1 0 . null -- | /O(1)/. The emptiness check for the 'Meep' -- -- >>> null empty -- True -- -- >>> null (singleton 4 "foo") -- False null :: Meep k a -> Bool null Empty = True null (Meep _ _) = False -- | /O(1)/. Build the 'Meep' -- -- @ -- fromMaybe ≡ view (from maybeing) -- @ fromMaybe :: Maybe (k, a) -> Meep k a fromMaybe = maybe Empty (uncurry Meep) -- | /O(1)/. Destroy the 'Meep' -- -- @ -- toMaybe ≡ view maybeing -- @ toMaybe :: Meep k a -> Maybe (k, a) toMaybe Empty = Nothing toMaybe (Meep k a) = Just (k, a) -- | /O(1)/. A witness to -- -- @ -- 'Meep' k v ≅ 'Maybe' (k, v) -- @ -- -- >>> singleton 4 "foo" ^. maybeing -- Just (4,"foo") -- -- >>> Nothing ^. from maybeing -- fromMaybe Nothing maybeing :: Iso (Meep k v) (Meep k' v') (Maybe (k, v)) (Maybe (k', v')) maybeing = iso toMaybe fromMaybe -- | /O(1)/. Return all keys from the 'Meep' -- -- >>> keys (singleton 4 "foo") -- Just 4 -- -- >>> keys empty -- Nothing keys :: Meep k a -> Maybe k keys Empty = Nothing keys (Meep k _) = Just k -- | /O(1)/. Return all values from the 'Meep' -- -- >>> elems (singleton 4 "foo") -- Just "foo" -- -- >>> elems empty -- Nothing elems :: Meep k a -> Maybe a elems Empty = Nothing elems (Meep _ a) = Just a insert :: Eq k => k -> a -> Meep k a -> Meep k a insert k a Empty = Meep k a insert k a x@(Meep k' _) = bool x (Meep k a) (k == k') lookup :: Eq k => k -> Meep k a -> Maybe a lookup _ Empty = Nothing lookup k' (Meep k a) = bool Nothing (Just a) (k == k') delete :: Eq k => k -> Meep k a -> Meep k a delete _ Empty = Empty delete k' x@(Meep k _) = bool x Empty (k == k') bool :: a -> a -> Bool -> a bool f t p = if p then t else f