packages feed

seonbi-0.3.0: src/Text/Seonbi/Trie.hs

{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
-- | A trie from 'Text' keys to values.
module Text.Seonbi.Trie
    ( Trie
    , elems
    , empty
    , fromList
    , insert
    , keys
    , lookup
    , member
    , mergeBy
    , null
    , singleton
    , size
    , toList
    , unionL
    , unionR
    ) where

import Prelude hiding (lookup, null)

import Control.Monad (ap)
import qualified GHC.Exts

import Data.ByteString (ByteString)
import Data.Text hiding (empty, null, singleton)
import Data.Text.Encoding (encodeUtf8, decodeUtf8)
import qualified Data.Trie as BTrie

-- | A trie from 'Text' keys to 'a' values.
newtype Trie a
  = Trie (BTrie.Trie a)
  deriving (Eq, Show)

encodeKey :: Text -> ByteString
encodeKey = encodeUtf8

decodeKey :: ByteString -> Text
decodeKey = decodeUtf8

-- | The empty trie.
empty :: Trie a
empty = Trie BTrie.empty

-- | Checks if the trie is empty.
null :: Trie a -> Bool
null (Trie btrie) = BTrie.null btrie

-- | Constructs a singleton trie.
singleton :: Text -> a -> Trie a
singleton key value = Trie $ BTrie.singleton (encodeKey key) value

-- | Gets the number of elements in the trie.
size :: Trie a -> Int
size (Trie btrie) = BTrie.size btrie

fromList' :: [(Text, a)] -> Trie a
fromList' list = Trie $ BTrie.fromList [(encodeKey k, v) | (k, v) <- list]

toList' :: Trie a -> [(Text, a)]
toList' (Trie btrie) = [(decodeKey k, v) | (k, v) <- BTrie.toList btrie]

-- | Converts a list of associated pairs into a trie.  For duplicate keys,
-- values earlier in the list shadow later ones.
fromList :: [(Text, a)] -> Trie a
fromList = fromList'

-- | Converts a trie into a list of associated pairs.  Keys will be ordered.
toList :: Trie a -> [(Text, a)]
toList = toList'

-- | Lists all keys in the trie.  Keys will be ordered.
keys :: Trie a -> [Text]
keys (Trie btrie) = Prelude.map decodeKey $ BTrie.keys btrie

-- | Lists all values in the trie.  Values are ordered by their associated keys.
elems :: Trie a -> [a]
elems (Trie btrie) = BTrie.elems btrie

-- | Gets the value associated with a key if it exists.
lookup :: Text -> Trie a -> Maybe a
lookup key (Trie btrie) = BTrie.lookup (encodeKey key) btrie

-- | Checks if a key has a value in the trie.
member :: Text -> Trie a -> Bool
member key (Trie btrie) = BTrie.member (encodeKey key) btrie

-- | Inserts a new key into the trie.
insert
    :: Text
    -- ^ A new key to insert.  If there is already the same key in the trie,
    -- the existing value is overwritten by the new value.
    -> a
    -- ^ A value associated to the key.
    -> Trie a
    -- ^ An existing trie.
    -> Trie a
    -- ^ The new trie with the inserted key.
insert key value (Trie btrie) = Trie $ BTrie.insert (encodeKey key) value btrie

-- | Combines two tries, using a function to resolve collisions.  This can only
-- define the space of functions between union and symmetric difference but,
-- with those two, all set operations can be defined (albeit inefficiently).
mergeBy :: (a -> a -> Maybe a) -> Trie a -> Trie a -> Trie a
mergeBy f (Trie a) (Trie b) = Trie $ BTrie.mergeBy f a b

-- | Combines two tries, resolving conflicts by choosing the value from the
-- left (former) trie.
unionL :: Trie a -> Trie a -> Trie a
unionL (Trie left) (Trie right) = Trie $ BTrie.unionL left right

-- | Combines two tries, resolving conflicts by choosing the value from the
-- right (latter) trie.
unionR :: Trie a -> Trie a -> Trie a
unionR (Trie left) (Trie right) = Trie $ BTrie.unionR left right

instance Functor Trie where
    fmap f (Trie btrie) = Trie $ fmap f btrie

instance Foldable Trie where
    foldMap f (Trie btrie) = foldMap f btrie

instance Traversable Trie where
    traverse f (Trie btrie) = Trie <$> traverse f btrie

instance Applicative Trie where
    pure = return
    (<*>) = ap

instance Monad Trie where
    return = singleton ""
    Trie btrie >>= f = Trie $ btrie >>= (\ v -> case f v of { Trie b -> b })

instance (Semigroup a) => Semigroup (Trie a) where
    (Trie a) <> (Trie b) = Trie (a <> b)

instance (Monoid a) => Monoid (Trie a) where
    mempty = Trie mempty
    mappend (Trie a) (Trie b) = Trie $ mappend a b

instance GHC.Exts.IsList (Trie a) where
    type Item (Trie a) = (Text, a)
    fromList = fromList'
    toList = toList'