{-# OPTIONS_GHC -Wno-unused-foralls #-}
{-# OPTIONS_GHC -Wno-unused-type-patterns #-}
{-# OPTIONS_GHC -Wno-redundant-constraints #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module MXNet.Core.Base.HMap
(
HMap
, KV (..)
, ShowKV (..)
, MatchKVList (..)
, nil
, add
, add'
, (.+.)
, get
, (.->.)
, update
, set
, mergeTo
, dump
) where
import GHC.TypeLits
import Data.List (intercalate)
import Data.Monoid ((<>))
import Data.Proxy (Proxy (..))
import Data.Typeable (Typeable, typeOf)
data KV v = Symbol := v
infixr 6 :=
data KVList (kvs :: [KV *]) where
Nil :: KVList '[]
Cons :: v -> KVList kvs -> KVList (k ':= v ': kvs)
data IfHasKey = Yes Symbol | No
type family FindKV (k :: Symbol) v (kvs :: [KV *]) :: IfHasKey where
FindKV k _ '[] = 'No
FindKV k v (k ':= v ': kvs) = 'Yes k
FindKV k1 v1 (k2 ':= v2 ': kvs) = FindKV k1 v1 kvs
newtype HMap (kvs :: [KV *]) = HMap { getKVList :: KVList kvs }
class InDict (k :: Symbol) (v :: *) (kvs :: [KV *]) | k kvs -> v where
get' :: HMap kvs -> v
update' :: (v -> v) -> HMap kvs -> HMap kvs
instance {-# OVERLAPPING #-} InDict k v (k ':= v ': kvs) where
get' (HMap (Cons v _)) = v
{-# INLINE get' #-}
update' f (HMap (Cons v kvs)) = HMap $ Cons (f v) kvs
{-# INLINE update' #-}
instance (InDict k v kvs, 'Yes k ~ FindKV k v (k' ':= v' ': kvs)) => InDict k v (k' ':= v' ': kvs) where
get' (HMap (Cons _ kvs)) = get' @k (HMap kvs)
{-# INLINE get' #-}
update' f (HMap (Cons v kvs)) = HMap $ Cons v (getKVList $ update' @k f (HMap kvs))
{-# INLINE update' #-}
nil :: HMap '[]
nil = HMap Nil
{-# INLINE nil #-}
add :: forall k v kvs. 'No ~ FindKV k v kvs => v -> HMap kvs -> HMap (k ':= v ': kvs)
add v (HMap kvs) = HMap (Cons v kvs)
{-# INLINE add #-}
add' :: forall k v kvs. Proxy k -> v -> HMap kvs -> HMap (k ':= v ': kvs)
add' _ v (HMap kvs) = HMap (Cons v kvs)
{-# INLINE add' #-}
(.+.) :: forall k v kvs. 'No ~ FindKV k v kvs => v -> HMap kvs -> HMap (k ':= v ': kvs)
(.+.) = add
infix 8 .+.
{-# INLINE (.+.) #-}
get :: forall (k :: Symbol) v kvs. InDict k v kvs => HMap kvs -> v
get = get' @k
{-# INLINE get #-}
(.->.) :: forall (k :: Symbol) v kvs. InDict k v kvs => HMap kvs -> v
(.->.) = get @k
infix 7 .->.
{-# INLINE (.->.) #-}
update :: forall (k :: Symbol) v kvs. InDict k v kvs => (v -> v) -> HMap kvs -> HMap kvs
update = update' @k
{-# INLINE update #-}
set :: forall k v kvs. InDict k v kvs => v -> HMap kvs -> HMap kvs
set v = update' @k (const v)
{-# INLINE set #-}
class MatchKVList (kvs1 :: [KV *]) (kvs2 :: [KV *]) where
mergeTo' :: HMap kvs1 -> HMap kvs2 -> HMap kvs2
instance MatchKVList ('[]) (kvs2) where
mergeTo' _ m2 = m2
instance (MatchKVList kvs1 kvs2, InDict k v kvs2) => MatchKVList (k ':= v ': kvs1) kvs2 where
mergeTo' (HMap (Cons v kvs)) m2 = mergeTo' (HMap kvs) (set @k v m2)
mergeTo :: forall (kvs1 :: [KV *]) (kvs2 :: [KV *]). MatchKVList kvs1 kvs2 => HMap kvs1 -> HMap kvs2 -> HMap kvs2
mergeTo = mergeTo'
class ShowKV (kvs :: [KV *]) where
show' :: forall k v. KVList kvs -> [(String, String)]
instance ShowKV '[] where
show' _ = []
{-# INLINE show' #-}
instance (KnownSymbol k, Typeable v, Show v, ShowKV kvs) => ShowKV (k ':= v ': kvs) where
show' (Cons v kvs') = showImpl v : show' kvs'
where showImpl value = (symbolVal (Proxy :: Proxy k), if typeOf value == typeOf "" then (init . tail . show) value else show value)
{-# INLINE show' #-}
instance ShowKV kvs => Show (HMap kvs) where
show m = "[" <> (intercalate ", " . map (\(k, v) -> k <> " = " <> v) . show' . getKVList $ m) <> "]"
{-# INLINE show #-}
dump :: forall kvs. ShowKV kvs => HMap kvs -> [(String, String)]
dump = show' . getKVList
{-# INLINE dump #-}