formura-1.0: src/Formura/Annotation.hs
{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
module Formura.Annotation where
import Control.Lens
import Control.Monad
import Data.Maybe
import Data.Dynamic
import Prelude hiding (map)
import qualified Prelude as P (map)
-- | A dynamically-typed list of annotations.
type Annotation = [Dynamic]
class Annotated a where
annotation :: Lens' a Annotation
instance Annotated Annotation where
annotation = simple
-- | An empty collection.
empty :: Annotation
empty = []
-- | An annotation from a single value
singleton :: Typeable a => a -> Annotation
singleton x = [toDyn x]
-- | Add an annotation to a collection.
insert :: (Typeable a) => a -> Annotation -> Annotation
insert x ys = toDyn x : ys
-- | Remove all elements of type @a@ from the collection, and
-- set @x@ as the only member of the type in the collection.
set :: (Typeable a) => a -> Annotation -> Annotation
set x ys = toDyn x : filter ((/= typeOf x) . dynTypeRep) ys
-- | set @x@ as the only member of the type in the collection,
-- only if no annotation of the same type pre-exists.
weakSet :: (Typeable a) => a -> Annotation -> Annotation
weakSet x ys
| any ((== typeOf x) . dynTypeRep) ys = ys
| otherwise = toDyn x : ys
-- | Extract all annotations of type @a@ from
-- the collection.
toList :: (Typeable a) => Annotation -> [a]
toList = catMaybes . P.map fromDynamic
-- | Extract the first annotation of the given type,
-- if it exists.
toMaybe :: (Typeable a) => Annotation -> Maybe a
toMaybe = msum . P.map fromDynamic
-- | Extract the first annotation of the given type,
-- if it exists.
viewMaybe :: (Typeable a, Annotated b) => b -> Maybe a
viewMaybe = toMaybe . (^. annotation)
-- | Map all annotations of type @a@ to type @b@,
-- while leaving the others untouched.
map :: (Typeable a, Typeable b) => (a->b) -> Annotation -> Annotation
map f = P.map (maybeApply f)
maybeApply :: (Typeable a, Typeable b) => (a->b) -> Dynamic -> Dynamic
maybeApply f x =
case dynApply (toDyn f) x of
Just y -> y
Nothing -> x