{-# LANGUAGE CPP , UnicodeSyntax , NoImplicitPrelude , DeriveDataTypeable #-} #if __GLASGOW_HASKELL__ >= 704 {-# LANGUAGE Safe #-} #endif ----------------------------------------------------------------------------- -- | -- Module : Data.DString -- Copyright : (c) 2009-2011 Bas van Dijk -- License : BSD-style (see the file LICENSE) -- Maintainer : Bas van Dijk -- Stability : experimental -- -- Difference strings: a data structure for O(1) append on strings. Note that a -- 'DString' is just a newtype wrapper around a 'DList' 'Char'. The reason we -- need a new type instead of just a type synonym is that we can have an -- @instance 'IsString' 'DString'@ without using language extensions -- (@TypeSynonymInstances@ or @FlexibleInstances@) so we can write overloaded -- string literals of type 'DString'. -- ----------------------------------------------------------------------------- module Data.DString ( DString -- * Conversion , toString , fromDList , toDList , fromShowS , toShowS -- * Basic functions , singleton , cons , snoc , concat , list , head , tail , unfoldr , foldr ) where -------------------------------------------------------------------------------- -- Imports -------------------------------------------------------------------------------- -- from base: import Prelude ( (>=), error ) import Data.Char ( Char ) import Data.Function ( ($), const, flip ) import Data.List ( map ) import Data.Maybe ( Maybe ) import Data.Monoid ( Monoid, mempty, mappend ) import Data.Typeable ( Typeable ) import Data.String ( IsString, fromString ) import Text.Show ( Show, showsPrec, ShowS, showParen, showString, shows ) #if MIN_VERSION_base(4,4,0) import Data.String ( String ) #else import Data.Char ( String ) #endif #if __GLASGOW_HASKELL__ < 700 import Prelude ( fromInteger ) #endif -- from base-unicode-symbols: import Data.Function.Unicode ( (∘) ) -- from dlist: import Data.DList ( DList(DL), unDL, toList, fromList ) import qualified Data.DList as D ( cons, snoc , foldr, unfoldr , singleton , concat ) -------------------------------------------------------------------------------- -- DString -------------------------------------------------------------------------------- -- | A difference string is a function that given a string, returns the original -- contents of the difference string prepended at the given string. -- -- This structure supports O(1) @mappend@ en @snoc@ operations on strings making -- it very usefull for append-heavy uses such as logging and pretty printing. -- -- You can use it to efficiently show a tree for example: (Note that we use some -- handy functions from the @string-combinators@ package) -- -- > {-# LANGUAGE OverloadedStrings #-} -- > -- > import Data.DString (toShowS, fromShowS) -- > import Data.String.Combinators ((<+>), parens, thenParens) -- > -- > data Tree a = Leaf a | Branch (Tree a) (Tree a) -- > -- > instance Show a => Show (Tree a) where -- > showsPrec prec t = toShowS $ (prec >= funAppPrec) `thenParens` go t -- > where -- > go (Leaf x) = "Leaf" <+> fromShowS (showsPrec funAppPrec x) -- > go (Branch l r) = "Branch" <+> parens (go l) <+> parens (go r) -- > -- > funAppPrec = 10 newtype DString = DS (DList Char) deriving (Typeable) instance Show DString where showsPrec p ds = showParen (p >= 10) $ showString "Data.String.fromString " ∘ shows (toString ds) instance Monoid DString where mempty = fromDList mempty ds1 `mappend` ds2 = fromDList $ toDList ds1 `mappend` toDList ds2 {-# INLINE mempty #-} {-# INLINE mappend #-} -------------------------------------------------------------------------------- -- Conversions -------------------------------------------------------------------------------- instance IsString DString where fromString = fromDList ∘ fromList {-# INLINE fromString #-} -- | O(n) Convert a difference string to a normal string. toString ∷ DString → String toString = toList ∘ toDList {-# INLINE toString #-} -- | O(1) Convert a difference list of @Char@s to a difference string. fromDList ∷ DList Char → DString fromDList = DS {-# INLINE fromDList #-} -- | O(1) Convert a difference string to a difference list. toDList ∷ DString → DList Char toDList (DS dl) = dl {-# INLINE toDList #-} -- | O(1) Convert a @ShowS@ to a difference string. fromShowS ∷ ShowS → DString fromShowS = fromDList ∘ DL {-# INLINE fromShowS #-} -- | O(1) Convert a difference string to a @ShowS@. toShowS ∷ DString → ShowS toShowS = unDL ∘ toDList {-# INLINE toShowS #-} -------------------------------------------------------------------------------- -- Basic functions -------------------------------------------------------------------------------- -- | O(1) Build a difference string from a single @Char@. singleton ∷ Char → DString singleton = fromDList ∘ D.singleton {-# INLINE singleton #-} -- | /O(1)/, Prepend a Char to a difference string. cons ∷ Char → DString → DString cons c ds = fromDList $ D.cons c (toDList ds) {-# INLINE cons #-} -- | /O(1)/, Append a @Char@ to a difference string. snoc ∷ DString → Char → DString snoc ds c = fromDList $ D.snoc (toDList ds) c {-# INLINE snoc #-} -- | /O(spine)/, Concatenate difference strings. concat ∷ [DString] → DString concat = fromDList ∘ D.concat ∘ map toDList {-# INLINE concat #-} -- | /O(length ds)/, difference list elimination, head, tail. list ∷ α → (Char → DString → α) → DString → α list nill consit ds = case toString ds of [] → nill x : xs → consit x $ fromString xs -- | Return the head of the difference string. head ∷ DString → Char head = list (error "Data.DString.head: empty list") const -- | Return the tail of the difference string. tail ∷ DString → DString tail = list (error "Data.DString.tail: empty list") (flip const) -- | Unfoldr for difference strings. unfoldr ∷ (α → Maybe (Char, α)) → α → DString unfoldr pf b = fromDList $ D.unfoldr pf b {-# INLINE unfoldr #-} -- | Foldr over difference strings. foldr ∷ (Char → α → α) → α → DString → α foldr f b = D.foldr f b ∘ toDList {-# INLINE foldr #-}