packages feed

fir-0.1.0.0: src/Data/Type/Map.hs

{-# LANGUAGE DataKinds              #-}
{-# LANGUAGE DerivingStrategies     #-}
{-# LANGUAGE GADTs                  #-}
{-# LANGUAGE PolyKinds              #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE TypeOperators          #-}
{-# LANGUAGE UndecidableInstances   #-}

{-|
Module: Data.Type.Map

Simple type-level maps (association lists).
-}

module Data.Type.Map where

-- base
import Data.Type.Bool
  ( If )
import GHC.TypeLits
  ( TypeError, ErrorMessage(..) )

-- fir
import Data.Type.POrd
  ( POrd(Compare, (:<)) )

------------------------------------------------
-- barebones type-level map functionality

infixr 4 :->
data (:->) k v = k :-> v
  deriving stock ( Eq, Show )

-- this synonym should only be used when we are working
-- with lists that are known to be ordered
-- (assumes keys are an instance of POrd)
type Map k v = [k :-> v]

type family Key (a :: (k :-> v)) :: k where
  Key (k ':-> _) = k

type family Value (a :: (k :-> v)) :: v where
  Value (_ ':-> v) = v

type family Keys (i :: [k :-> v]) :: [k] where
  Keys '[] = '[]
  Keys (kv ': kvs) = Key kv ': Keys kvs

type family Values (i :: [k :-> v]) :: [v] where
  Values '[] = '[]
  Values (kv ': kvs) = Value kv ': Values kvs

-- looks through the whole list always, in case the list is not sorted
type family Lookup (s :: k) (i :: [k :-> v]) :: Maybe v where
  Lookup _ '[]               = 'Nothing
  Lookup k ((k ':-> a) ': _) = 'Just a
  Lookup k (_          ': b) = Lookup k b

type family LookupKey (s :: k) (i :: [k :-> v]) :: Maybe k where
  LookupKey _ '[]               = 'Nothing
  LookupKey k ((k ':-> _) ': _) = 'Just k
  LookupKey k (_          ': b) = LookupKey k b

-- | Insert a key/value pair in an already-sorted map.
--
-- Throws an error if the key is already present.
type family Insert (s :: k) (t :: v) (i :: Map k v) :: Map k v where
  Insert k v '[]              = '[ k ':-> v ]
  Insert k v ((k ':-> a) ': _) =
    TypeError
      (     Text "Duplicate key: "
       :$$: ShowType '[ (k ':-> v), (k ':-> a) ]
      )
  Insert k v ((l ':-> a) ': b) =
    If ( k :< l )
      ( (k ':-> v) ': (l ':-> a) ': b )
      ( (l ':-> a) ': Insert k v b )

-- | Insert a key/value pair in an already-sorted map.
--
-- If a value already exists with that key, it gets overwritten with the new value.
type family InsertOverwriting (s :: k) (t :: v) (i :: Map k v) :: Map k v where
  InsertOverwriting k v '[]               = '[ k ':-> v ]
  InsertOverwriting k v ((k ':-> a) ': b) = (k ':-> v) ': b
  InsertOverwriting k v ((l ':-> a) ': b) =
    If ( k :< l )
      ( (k ':-> v) ': (l ':-> a) ': b )
      ( (l ':-> a) ': InsertOverwriting k v b )

type family InsertWithAppend ( key :: k ) ( val :: v ) ( map :: Map k [v] ) :: Map k [v] where
  InsertWithAppend key val '[] = '[ key ':-> '[ val ] ]
  InsertWithAppend key val ( ( k ':-> vals ) ': kvs ) =
    InsertWithAppendWithComparison key val k vals kvs ( Compare key k )

type family InsertWithAppendWithComparison ( key :: k ) ( val :: v ) ( key' :: k ) ( vals :: [v] ) ( rest :: Map k [v] ) ( comp :: Ordering ) :: Map k [v] where
  InsertWithAppendWithComparison key val key' vals rest LT =
    ( ( key ':-> '[val] ) ': ( key' ':-> vals ) ': rest )
  InsertWithAppendWithComparison key val key' vals rest EQ =
    ( ( key ':-> ( val ': vals ) ) ': rest )
  InsertWithAppendWithComparison key val key' vals rest GT =
    ( ( key' ':-> vals ) ': InsertWithAppend key val rest )


type family Union (i :: Map k v) (j :: Map k v) :: Map k v where
  Union i '[]                = i
  Union i ( (k ':-> a) ': b) = Union (Insert k a i) b

type family UnionWithAppend (i :: Map k [v]) (j :: Map k [v]) :: Map k [v] where
  UnionWithAppend i '[] = i
  UnionWithAppend '[] i = i
  UnionWithAppend ((k1 ':-> '[]) ': kvs1) i = UnionWithAppend kvs1 i
  UnionWithAppend ((k1 ':-> val ': vals1) ': kvs1) ( ( k2 ':-> vals2 ) ': kvs2 ) =
    UnionWithAppend  ((k1 ':-> vals1) ': kvs1) (InsertWithAppendWithComparison k1 val k2 vals2 kvs2 (Compare k1 k2))

type family Delete (s :: k) (is :: Map k v) :: Map k v where
  Delete _ '[]                 = '[]
  Delete k ( (k ':-> _) ': is) = is -- assumes there are no duplicates
  Delete k ( i          ': is) = i ': Delete k is

type family Remove (i :: Map k v) (j :: Map k v) :: Map k v where
  Remove '[]                j = j
  Remove ( (k ':-> _) ': i) j = Remove i (Delete k j)

type family InsertionSort (i :: [k :-> v]) :: Map k v where
  InsertionSort '[]              = '[]
  InsertionSort ((k ':-> v) : l) = Insert k v (InsertionSort l)

type family ZipValue (i :: [k]) (x :: v) :: Map k v where
  ZipValue '[]       _ = '[]
  ZipValue (a ': as) x = ( (a ':-> x) ': ZipValue as x )

type family LookupAndLookup (key1 :: k1) (key2 :: k2) (map :: Map k1 (Map k2 v)) :: Maybe v where
  LookupAndLookup key1 key2 map = LookupAndLookupFrom key2 (Lookup key1 map)

type family LookupAndLookupFrom (key2 :: k2) (lk :: Maybe (Map k2 v)) :: Maybe b where
  LookupAndLookupFrom _    Nothing    = Nothing
  LookupAndLookupFrom key2 (Just map) = Lookup key2 map

type family InsertWithInsert (key1 :: k1) (key2 :: k2) (val :: v) (map :: Map k1 (Map k2 v)) :: Map k1 (Map k2 v) where
  InsertWithInsert key1 key2 val '[] = '[ key1 ':-> '[ key2 ':-> val ] ]
  InsertWithInsert key1 key2 val ((key1 ':-> map) ': rest) = ( key1 ':-> Insert key2 val map ) ': rest
  InsertWithInsert key1 key2 val ((key ':-> map) ': rest) =
    If (key1 :< key)
      ((key1 ':-> '[ key2 ':-> val ]) ': (key ':-> map) ': rest)
      ((key ':-> map) ': InsertWithInsert key1 key2 val rest)