packages feed

FiniteCategories-0.1.0.0: src/Utils/AssociationList.hs

{-| Module  : FiniteCategories
Description : The type for association lists.
Copyright   : Guillaume Sabbagh 2021
License     : GPL-3
Maintainer  : guillaumesabbagh@protonmail.com
Stability   : experimental
Portability : portable

The type for association lists.

It is used when the 'Ord' constraint of `Data.Map` is too restrictive.
-}

module Utils.AssociationList
(
    AssociationList,
    keys,
    values,
    (!-),
    (!-!),
    (!-?),
    (!-.),
    mkAssocListIdentity,
    enumAssocLists,
    functToAssocList,
    assocListToFunct,
    inverse,
    removeKey,
    removeValue,
)
where
    import Utils.CartesianProduct
    import Data.Tuple (swap)
    
    -- | The type of association lists (a list of couples).
    type AssociationList a b = [(a, b)]
    
    -- | Returns the keys of the association list.
    keys :: (AssociationList a b) -> [a]
    keys = fmap fst
    
    -- | Returns the values of the association list.
    values :: (AssociationList a b) -> [b]
    values = fmap snd
    
    -- | If the key is in the association list, returns Just the value associated, otherwise Nothing.
    --
    -- Same as lookup in `Data.Map`.
    (!-) :: (Eq a) => a -> (AssociationList a b) -> Maybe b
    (!-) _ [] = Nothing
    (!-) k ((a,b):xs)
        | a == k = Just b
        | otherwise = k !- xs
        
    -- | If the key is in the association list, returns the value associated, otherwise throws an error.
    --
    -- Same as (!) in `Data.Map`.
    (!-!) :: (Eq a) => (AssociationList a b) -> a -> b
    (!-!) [] _ = error "Key not in association list."
    (!-!) ((a,b):xs) k
        | a == k = b
        | otherwise = xs !-! k
      
    -- | If the key is in the association list, returns the value associated, otherwise returns a default value.
    --
    -- Same as /findWithDefault/ in `Data.Map`.      
    (!-?) :: (Eq a) => b -> a -> (AssociationList a b) -> b
    (!-?) d _ [] = d
    (!-?) d k ((a,b):xs)
        | a == k = b
        | otherwise = (!-?) d k xs
                               
    -- | Composition of association lists.
    (!-.) :: (Eq a, Eq b) => (AssociationList b c) -> (AssociationList a b) -> (AssociationList a c)
    (!-.) al2 al1 = [(k, al2 !-! (al1 !-! k)) | k <- keys al1, elem (al1 !-! k) (keys al2)]
    
    -- | Constructs the identity association list of a list of values.
    -- 
    -- For example, @ mkAssocListIdentity [1,2,3] = [(1,1),(2,2),(3,3)]@
    mkAssocListIdentity :: [a] -> AssociationList a a
    mkAssocListIdentity xs = [(o,o) | o <- xs]
    
    -- | Enumerates all association lists possible between a domain and a codomain.
    enumAssocLists :: [a] -> [b] -> [AssociationList a b]
    enumAssocLists dom codom = [zip dom im | im <- (codom |^| (length dom))]
    
    -- | Transforms a function and a domain into an association list.
    functToAssocList :: (a -> b) -> [a] -> (AssociationList a b)
    functToAssocList f d = [(o, f o) | o <- d]
    
    -- | Transforms an association list to a function.
    assocListToFunct :: (Eq a) => (AssociationList a b) -> a -> b
    assocListToFunct [] _ = error "Can't transform an empty list into a function."
    assocListToFunct ((k,v):xs) x
        | k == x = v
        | otherwise = assocListToFunct xs x
        
    -- | Inverse of an association list
    inverse :: (AssociationList a b) -> (AssociationList b a)
    inverse kvs = swap <$> kvs
    
    -- | Remove all couples with a certain key
    removeKey :: (Eq a) => (AssociationList a b) -> a -> (AssociationList a b)
    removeKey al key = [c | c@(k,_) <- al, k /= key]
    
    -- | Remove all couples with a certain value
    removeValue :: (Eq b) => (AssociationList a b) -> b -> (AssociationList a b)
    removeValue al value = [c | c@(_,v) <- al, v /= value]