{-|
Module      : Prosidy.Types.Assoc
Description : An associative mapping of keys to values.
Copyright   : ©2020 James Alexander Feldman-Crough
License     : MPL-2.0
Maintainer  : alex@fldcr.com
-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE OverloadedStrings #-}
module Prosidy.Types.Assoc
    ( Assoc(..)
    , asHashMap
    , fromHashMap
    , toHashMap
    , toEntries
    )
where

import           Prosidy.Internal.Classes

import           Data.HashMap.Strict            ( HashMap )

import qualified Data.HashMap.Strict           as HM
import qualified Data.Text.Prettyprint.Doc     as PP

-- | An associative mapping of keys to values.
--
-- Currently implemented as a 'HashMap', this newtype wrapper allows us to:
--
-- 1) Add non-orphan instances to the underlying structure.
-- 2) Change the underlying type if needed.
newtype Assoc k v = Assoc (HashMap k v)
  deriving ((forall x. Assoc k v -> Rep (Assoc k v) x)
-> (forall x. Rep (Assoc k v) x -> Assoc k v)
-> Generic (Assoc k v)
forall x. Rep (Assoc k v) x -> Assoc k v
forall x. Assoc k v -> Rep (Assoc k v) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall k v x. Rep (Assoc k v) x -> Assoc k v
forall k v x. Assoc k v -> Rep (Assoc k v) x
$cto :: forall k v x. Rep (Assoc k v) x -> Assoc k v
$cfrom :: forall k v x. Assoc k v -> Rep (Assoc k v) x
Generic)
  deriving (Assoc k v -> Assoc k v -> Bool
(Assoc k v -> Assoc k v -> Bool)
-> (Assoc k v -> Assoc k v -> Bool) -> Eq (Assoc k v)
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
forall k v. (Eq k, Eq v) => Assoc k v -> Assoc k v -> Bool
/= :: Assoc k v -> Assoc k v -> Bool
$c/= :: forall k v. (Eq k, Eq v) => Assoc k v -> Assoc k v -> Bool
== :: Assoc k v -> Assoc k v -> Bool
$c== :: forall k v. (Eq k, Eq v) => Assoc k v -> Assoc k v -> Bool
Eq, Int -> Assoc k v -> ShowS
[Assoc k v] -> ShowS
Assoc k v -> String
(Int -> Assoc k v -> ShowS)
-> (Assoc k v -> String)
-> ([Assoc k v] -> ShowS)
-> Show (Assoc k v)
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
forall k v. (Show k, Show v) => Int -> Assoc k v -> ShowS
forall k v. (Show k, Show v) => [Assoc k v] -> ShowS
forall k v. (Show k, Show v) => Assoc k v -> String
showList :: [Assoc k v] -> ShowS
$cshowList :: forall k v. (Show k, Show v) => [Assoc k v] -> ShowS
show :: Assoc k v -> String
$cshow :: forall k v. (Show k, Show v) => Assoc k v -> String
showsPrec :: Int -> Assoc k v -> ShowS
$cshowsPrec :: forall k v. (Show k, Show v) => Int -> Assoc k v -> ShowS
Show, [Assoc k v] -> Encoding
[Assoc k v] -> Value
Assoc k v -> Encoding
Assoc k v -> Value
(Assoc k v -> Value)
-> (Assoc k v -> Encoding)
-> ([Assoc k v] -> Value)
-> ([Assoc k v] -> Encoding)
-> ToJSON (Assoc k v)
forall a.
(a -> Value)
-> (a -> Encoding)
-> ([a] -> Value)
-> ([a] -> Encoding)
-> ToJSON a
forall k v. (ToJSON v, ToJSONKey k) => [Assoc k v] -> Encoding
forall k v. (ToJSON v, ToJSONKey k) => [Assoc k v] -> Value
forall k v. (ToJSON v, ToJSONKey k) => Assoc k v -> Encoding
forall k v. (ToJSON v, ToJSONKey k) => Assoc k v -> Value
toEncodingList :: [Assoc k v] -> Encoding
$ctoEncodingList :: forall k v. (ToJSON v, ToJSONKey k) => [Assoc k v] -> Encoding
toJSONList :: [Assoc k v] -> Value
$ctoJSONList :: forall k v. (ToJSON v, ToJSONKey k) => [Assoc k v] -> Value
toEncoding :: Assoc k v -> Encoding
$ctoEncoding :: forall k v. (ToJSON v, ToJSONKey k) => Assoc k v -> Encoding
toJSON :: Assoc k v -> Value
$ctoJSON :: forall k v. (ToJSON v, ToJSONKey k) => Assoc k v -> Value
ToJSON, Value -> Parser [Assoc k v]
Value -> Parser (Assoc k v)
(Value -> Parser (Assoc k v))
-> (Value -> Parser [Assoc k v]) -> FromJSON (Assoc k v)
forall a.
(Value -> Parser a) -> (Value -> Parser [a]) -> FromJSON a
forall k v.
(FromJSON v, FromJSONKey k, Eq k, Hashable k) =>
Value -> Parser [Assoc k v]
forall k v.
(FromJSON v, FromJSONKey k, Eq k, Hashable k) =>
Value -> Parser (Assoc k v)
parseJSONList :: Value -> Parser [Assoc k v]
$cparseJSONList :: forall k v.
(FromJSON v, FromJSONKey k, Eq k, Hashable k) =>
Value -> Parser [Assoc k v]
parseJSON :: Value -> Parser (Assoc k v)
$cparseJSON :: forall k v.
(FromJSON v, FromJSONKey k, Eq k, Hashable k) =>
Value -> Parser (Assoc k v)
FromJSON, Assoc k v -> ()
(Assoc k v -> ()) -> NFData (Assoc k v)
forall a. (a -> ()) -> NFData a
forall k v. (NFData k, NFData v) => Assoc k v -> ()
rnf :: Assoc k v -> ()
$crnf :: forall k v. (NFData k, NFData v) => Assoc k v -> ()
NFData, b -> Assoc k v -> Assoc k v
NonEmpty (Assoc k v) -> Assoc k v
Assoc k v -> Assoc k v -> Assoc k v
(Assoc k v -> Assoc k v -> Assoc k v)
-> (NonEmpty (Assoc k v) -> Assoc k v)
-> (forall b. Integral b => b -> Assoc k v -> Assoc k v)
-> Semigroup (Assoc k v)
forall b. Integral b => b -> Assoc k v -> Assoc k v
forall a.
(a -> a -> a)
-> (NonEmpty a -> a)
-> (forall b. Integral b => b -> a -> a)
-> Semigroup a
forall k v. (Eq k, Hashable k) => NonEmpty (Assoc k v) -> Assoc k v
forall k v.
(Eq k, Hashable k) =>
Assoc k v -> Assoc k v -> Assoc k v
forall k v b.
(Eq k, Hashable k, Integral b) =>
b -> Assoc k v -> Assoc k v
stimes :: b -> Assoc k v -> Assoc k v
$cstimes :: forall k v b.
(Eq k, Hashable k, Integral b) =>
b -> Assoc k v -> Assoc k v
sconcat :: NonEmpty (Assoc k v) -> Assoc k v
$csconcat :: forall k v. (Eq k, Hashable k) => NonEmpty (Assoc k v) -> Assoc k v
<> :: Assoc k v -> Assoc k v -> Assoc k v
$c<> :: forall k v.
(Eq k, Hashable k) =>
Assoc k v -> Assoc k v -> Assoc k v
Semigroup, Semigroup (Assoc k v)
Assoc k v
Semigroup (Assoc k v) =>
Assoc k v
-> (Assoc k v -> Assoc k v -> Assoc k v)
-> ([Assoc k v] -> Assoc k v)
-> Monoid (Assoc k v)
[Assoc k v] -> Assoc k v
Assoc k v -> Assoc k v -> Assoc k v
forall a.
Semigroup a =>
a -> (a -> a -> a) -> ([a] -> a) -> Monoid a
forall k v. (Eq k, Hashable k) => Semigroup (Assoc k v)
forall k v. (Eq k, Hashable k) => Assoc k v
forall k v. (Eq k, Hashable k) => [Assoc k v] -> Assoc k v
forall k v.
(Eq k, Hashable k) =>
Assoc k v -> Assoc k v -> Assoc k v
mconcat :: [Assoc k v] -> Assoc k v
$cmconcat :: forall k v. (Eq k, Hashable k) => [Assoc k v] -> Assoc k v
mappend :: Assoc k v -> Assoc k v -> Assoc k v
$cmappend :: forall k v.
(Eq k, Hashable k) =>
Assoc k v -> Assoc k v -> Assoc k v
mempty :: Assoc k v
$cmempty :: forall k v. (Eq k, Hashable k) => Assoc k v
$cp1Monoid :: forall k v. (Eq k, Hashable k) => Semigroup (Assoc k v)
Monoid, Int -> Assoc k v -> Int
Assoc k v -> Int
(Int -> Assoc k v -> Int)
-> (Assoc k v -> Int) -> Hashable (Assoc k v)
forall a. (Int -> a -> Int) -> (a -> Int) -> Hashable a
forall k v. (Hashable k, Hashable v) => Int -> Assoc k v -> Int
forall k v. (Hashable k, Hashable v) => Assoc k v -> Int
hash :: Assoc k v -> Int
$chash :: forall k v. (Hashable k, Hashable v) => Assoc k v -> Int
hashWithSalt :: Int -> Assoc k v -> Int
$chashWithSalt :: forall k v. (Hashable k, Hashable v) => Int -> Assoc k v -> Int
Hashable) via HashMap k v
  deriving (a -> Assoc k a -> Bool
Assoc k m -> m
Assoc k a -> [a]
Assoc k a -> Bool
Assoc k a -> Int
Assoc k a -> a
Assoc k a -> a
Assoc k a -> a
Assoc k a -> a
(a -> m) -> Assoc k a -> m
(a -> m) -> Assoc k a -> m
(a -> b -> b) -> b -> Assoc k a -> b
(a -> b -> b) -> b -> Assoc k a -> b
(b -> a -> b) -> b -> Assoc k a -> b
(b -> a -> b) -> b -> Assoc k a -> b
(a -> a -> a) -> Assoc k a -> a
(a -> a -> a) -> Assoc k a -> a
(forall m. Monoid m => Assoc k m -> m)
-> (forall m a. Monoid m => (a -> m) -> Assoc k a -> m)
-> (forall m a. Monoid m => (a -> m) -> Assoc k a -> m)
-> (forall a b. (a -> b -> b) -> b -> Assoc k a -> b)
-> (forall a b. (a -> b -> b) -> b -> Assoc k a -> b)
-> (forall b a. (b -> a -> b) -> b -> Assoc k a -> b)
-> (forall b a. (b -> a -> b) -> b -> Assoc k a -> b)
-> (forall a. (a -> a -> a) -> Assoc k a -> a)
-> (forall a. (a -> a -> a) -> Assoc k a -> a)
-> (forall a. Assoc k a -> [a])
-> (forall a. Assoc k a -> Bool)
-> (forall a. Assoc k a -> Int)
-> (forall a. Eq a => a -> Assoc k a -> Bool)
-> (forall a. Ord a => Assoc k a -> a)
-> (forall a. Ord a => Assoc k a -> a)
-> (forall a. Num a => Assoc k a -> a)
-> (forall a. Num a => Assoc k a -> a)
-> Foldable (Assoc k)
forall a. Eq a => a -> Assoc k a -> Bool
forall a. Num a => Assoc k a -> a
forall a. Ord a => Assoc k a -> a
forall m. Monoid m => Assoc k m -> m
forall a. Assoc k a -> Bool
forall a. Assoc k a -> Int
forall a. Assoc k a -> [a]
forall a. (a -> a -> a) -> Assoc k a -> a
forall k a. Eq a => a -> Assoc k a -> Bool
forall k a. Num a => Assoc k a -> a
forall k a. Ord a => Assoc k a -> a
forall m a. Monoid m => (a -> m) -> Assoc k a -> m
forall k m. Monoid m => Assoc k m -> m
forall k a. Assoc k a -> Bool
forall k a. Assoc k a -> Int
forall k a. Assoc k a -> [a]
forall b a. (b -> a -> b) -> b -> Assoc k a -> b
forall a b. (a -> b -> b) -> b -> Assoc k a -> b
forall k a. (a -> a -> a) -> Assoc k a -> a
forall k m a. Monoid m => (a -> m) -> Assoc k a -> m
forall k b a. (b -> a -> b) -> b -> Assoc k a -> b
forall k a b. (a -> b -> b) -> b -> Assoc k a -> b
forall (t :: * -> *).
(forall m. Monoid m => t m -> m)
-> (forall m a. Monoid m => (a -> m) -> t a -> m)
-> (forall m a. Monoid m => (a -> m) -> t a -> m)
-> (forall a b. (a -> b -> b) -> b -> t a -> b)
-> (forall a b. (a -> b -> b) -> b -> t a -> b)
-> (forall b a. (b -> a -> b) -> b -> t a -> b)
-> (forall b a. (b -> a -> b) -> b -> t a -> b)
-> (forall a. (a -> a -> a) -> t a -> a)
-> (forall a. (a -> a -> a) -> t a -> a)
-> (forall a. t a -> [a])
-> (forall a. t a -> Bool)
-> (forall a. t a -> Int)
-> (forall a. Eq a => a -> t a -> Bool)
-> (forall a. Ord a => t a -> a)
-> (forall a. Ord a => t a -> a)
-> (forall a. Num a => t a -> a)
-> (forall a. Num a => t a -> a)
-> Foldable t
product :: Assoc k a -> a
$cproduct :: forall k a. Num a => Assoc k a -> a
sum :: Assoc k a -> a
$csum :: forall k a. Num a => Assoc k a -> a
minimum :: Assoc k a -> a
$cminimum :: forall k a. Ord a => Assoc k a -> a
maximum :: Assoc k a -> a
$cmaximum :: forall k a. Ord a => Assoc k a -> a
elem :: a -> Assoc k a -> Bool
$celem :: forall k a. Eq a => a -> Assoc k a -> Bool
length :: Assoc k a -> Int
$clength :: forall k a. Assoc k a -> Int
null :: Assoc k a -> Bool
$cnull :: forall k a. Assoc k a -> Bool
toList :: Assoc k a -> [a]
$ctoList :: forall k a. Assoc k a -> [a]
foldl1 :: (a -> a -> a) -> Assoc k a -> a
$cfoldl1 :: forall k a. (a -> a -> a) -> Assoc k a -> a
foldr1 :: (a -> a -> a) -> Assoc k a -> a
$cfoldr1 :: forall k a. (a -> a -> a) -> Assoc k a -> a
foldl' :: (b -> a -> b) -> b -> Assoc k a -> b
$cfoldl' :: forall k b a. (b -> a -> b) -> b -> Assoc k a -> b
foldl :: (b -> a -> b) -> b -> Assoc k a -> b
$cfoldl :: forall k b a. (b -> a -> b) -> b -> Assoc k a -> b
foldr' :: (a -> b -> b) -> b -> Assoc k a -> b
$cfoldr' :: forall k a b. (a -> b -> b) -> b -> Assoc k a -> b
foldr :: (a -> b -> b) -> b -> Assoc k a -> b
$cfoldr :: forall k a b. (a -> b -> b) -> b -> Assoc k a -> b
foldMap' :: (a -> m) -> Assoc k a -> m
$cfoldMap' :: forall k m a. Monoid m => (a -> m) -> Assoc k a -> m
foldMap :: (a -> m) -> Assoc k a -> m
$cfoldMap :: forall k m a. Monoid m => (a -> m) -> Assoc k a -> m
fold :: Assoc k m -> m
$cfold :: forall k m. Monoid m => Assoc k m -> m
Foldable, a -> Assoc k b -> Assoc k a
(a -> b) -> Assoc k a -> Assoc k b
(forall a b. (a -> b) -> Assoc k a -> Assoc k b)
-> (forall a b. a -> Assoc k b -> Assoc k a) -> Functor (Assoc k)
forall a b. a -> Assoc k b -> Assoc k a
forall a b. (a -> b) -> Assoc k a -> Assoc k b
forall k a b. a -> Assoc k b -> Assoc k a
forall k a b. (a -> b) -> Assoc k a -> Assoc k b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: a -> Assoc k b -> Assoc k a
$c<$ :: forall k a b. a -> Assoc k b -> Assoc k a
fmap :: (a -> b) -> Assoc k a -> Assoc k b
$cfmap :: forall k a b. (a -> b) -> Assoc k a -> Assoc k b
Functor) via HashMap k

instance (Eq k, Hashable k, Binary k, Binary v) => Binary (Assoc k v) where
    get :: Get (Assoc k v)
get = HashMap k v -> Assoc k v
forall k v. HashMap k v -> Assoc k v
Assoc (HashMap k v -> Assoc k v)
-> ([(k, v)] -> HashMap k v) -> [(k, v)] -> Assoc k v
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(k, v)] -> HashMap k v
forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HM.fromList ([(k, v)] -> Assoc k v) -> Get [(k, v)] -> Get (Assoc k v)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get [(k, v)]
forall t. Binary t => Get t
get

    put :: Assoc k v -> Put
put (Assoc hm :: HashMap k v
hm) = [(k, v)] -> Put
forall t. Binary t => t -> Put
put ([(k, v)] -> Put) -> [(k, v)] -> Put
forall a b. (a -> b) -> a -> b
$ HashMap k v -> [(k, v)]
forall k v. HashMap k v -> [(k, v)]
HM.toList HashMap k v
hm

instance (Pretty k, Pretty v) => Pretty (Assoc k v) where
    pretty :: Assoc k v -> Doc ann
pretty (Assoc hm :: HashMap k v
hm) =
        [Doc ann] -> Doc ann
forall ann. [Doc ann] -> Doc ann
PP.list
            ([Doc ann] -> Doc ann)
-> ([(k, v)] -> [Doc ann]) -> [(k, v)] -> Doc ann
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((k, v) -> Doc ann) -> [(k, v)] -> [Doc ann]
forall a b. (a -> b) -> [a] -> [b]
map (\(k :: k
k, v :: v
v) -> k -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty k
k Doc ann -> Doc ann -> Doc ann
forall ann. Doc ann -> Doc ann -> Doc ann
PP.<+> Doc ann
forall ann. Doc ann
PP.equals Doc ann -> Doc ann -> Doc ann
forall ann. Doc ann -> Doc ann -> Doc ann
PP.<+> v -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty v
v)
            ([(k, v)] -> Doc ann) -> [(k, v)] -> Doc ann
forall a b. (a -> b) -> a -> b
$ HashMap k v -> [(k, v)]
forall k v. HashMap k v -> [(k, v)]
HM.toList HashMap k v
hm

-- | Given a function which operates on a 'HashMap', return a function which
-- performs an equivalent transfromation on an 'Assoc'.
asHashMap
    :: Functor f
    => (HashMap k v -> f (HashMap k' v'))
    -> Assoc k v
    -> f (Assoc k' v')
asHashMap :: (HashMap k v -> f (HashMap k' v')) -> Assoc k v -> f (Assoc k' v')
asHashMap f :: HashMap k v -> f (HashMap k' v')
f (Assoc a :: HashMap k v
a) = HashMap k' v' -> Assoc k' v'
forall k v. HashMap k v -> Assoc k v
Assoc (HashMap k' v' -> Assoc k' v')
-> f (HashMap k' v') -> f (Assoc k' v')
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HashMap k v -> f (HashMap k' v')
f HashMap k v
a

-- | Convert a 'HashMap' to an 'Assoc'.
fromHashMap :: HashMap k v -> Assoc k v
fromHashMap :: HashMap k v -> Assoc k v
fromHashMap = HashMap k v -> Assoc k v
forall k v. HashMap k v -> Assoc k v
Assoc

-- | Convert an 'Assoc' to a 'HashMap'.
toHashMap :: Assoc k v -> HashMap k v
toHashMap :: Assoc k v -> HashMap k v
toHashMap (Assoc hm :: HashMap k v
hm) = HashMap k v
hm

-- | Convert an 'Assoc' into a list of key/value pairs.
toEntries :: Assoc k v -> [(k, v)]
toEntries :: Assoc k v -> [(k, v)]
toEntries (Assoc hm :: HashMap k v
hm) = HashMap k v -> [(k, v)]
forall k v. HashMap k v -> [(k, v)]
HM.toList HashMap k v
hm