uniform-strings (empty) → 0.1.0
raw patch · 8 files changed
+1061/−0 lines, 8 filesdep +MissingHdep +basedep +bytestringsetup-changed
Dependencies added: MissingH, base, bytestring, monads-tf, network-uri, pretty-show, safe, snap-core, split, text, text-icu, uniform-algebras
Files
- ChangeLog.md +5/−0
- README.md +50/−0
- Setup.lhs +6/−0
- Uniform/Strings.hs +49/−0
- Uniform/Strings/Conversion.hs +362/−0
- Uniform/Strings/Infix.hs +67/−0
- Uniform/Strings/Utilities.hs +458/−0
- uniform-strings.cabal +64/−0
+ ChangeLog.md view
@@ -0,0 +1,5 @@+0.0.12+ move from versions in Workspace8 2019+0.0.9 radical reduction to use Text only+0.0.11 structure regularized+ added LanguageCodes, needs uniform-algebras
+ README.md view
@@ -0,0 +1,50 @@+ ++# The uniform-strings package ++Tries to resolve the confusing breadth of choices to represent and operate on text in Haskell. It contains ++- a /principled/ set of conversions between representations for text, namely ++ - s - Strings,++ - t - Text,++ - b - ByteString,++ - l - LazyByteString, ++ - bu - ByteString containing UTF-8 characters,++ - latin - charcters with latin1 encoding++ - u - URLencoding, and ++conversions functions of type `x2y` (e.g. `s2t` for a conversion from String to Text) and methods to circumvent the difficulties.++- a set of functions applicable to character strings, which have the exact same semantics for all the types (usually the name of the function applicable to Strings with an appended \"\'\" to avoid name clashes with existing code.++- a few infix function for string manipulation. ++This is a package in `uniformBase` and other uniform packages will build on it. +++To Do:+The code originated in 2010 to 2018. It will be revised and the dependencies greatly reduced when using `text` and `basement`.++# Intention of "uniform" packages+The "uniform" packages are yet another attempt to select a useful subset from the overwhelming variety of the Haskell biotop. It was started in the 2010, grew over the years but was never packaged and put into Hackage; it is comparable to other similar attempts from which it has learned and occasionally copied code. ++The "uniform" approach is different from some others by:+- compatible with 'standard' Haskell, i.e. Haskell 2010 plus extensions as indicated in the modules,+- use the regular Haskell prelude,+- avoid name clashes as far as possible,+- combine logically connected operations in one place and in a form allowing coordinated use, but +- broken in small, mostly independently building packages.++Choices are:+- strong preference for total functions, achieved often with producing error messages which makes debugging easier than the plain failure of partial functions,+- use monads-tf because TypeFamilies seemed to lead to better documentation.++Issues with this approach: it is limited by the deeps of understanding of Haskell of the authors and his experience. It shows a focus on understanding semantics (and formal ontology) linked to algebra applied to practical problems (Geographic Information Systems). +It seems that efforts to construct coherent subsets of Haskell are limited by the complexity of the task -- the more comprehensive an environment should be the more complex is it to learn and use. The approach here is what emerged after some 25 years of using Haskell to write application oriented code, mostly to demonstrate theories in spatial information theory.
+ Setup.lhs view
@@ -0,0 +1,6 @@+#!/usr/bin/runhaskell +> module Main where+> import Distribution.Simple+> main :: IO ()+> main = defaultMain+
+ Uniform/Strings.hs view
@@ -0,0 +1,49 @@+ --------------------------------------------------------------------+--+-- Module : Strings+-- Copyright :+--+-- | a top module exporting the operations in the package+----------------------------------------------------------------------+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PackageImports #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS_GHC -fno-warn-missing-methods #-}+{-# OPTIONS_GHC -w #-}++module Uniform.Strings (+ module Uniform.Strings.Conversion+ , module Uniform.Strings.Utilities+ , module Uniform.Strings.Infix+ , module Data.String + , putIOwords+ , NiceStrings (..)+ , IsString (..)+ -- , module Uniform.Zero+ , module Uniform.ListForm+ , ppShowList, ppShow+ ) where+++import "monads-tf" Control.Monad.State (MonadIO, liftIO)+import Data.List as L ()+import qualified Data.List.Split as S+--if these string ops are desired (and not the usual ones from fileio+-- then import them from Data.StringInfix++import Data.String+ ( IsString(..), lines, unlines, unwords, words, String )+import qualified Data.Text as T++import qualified Data.Text.IO as T +import Uniform.ListForm ( ListForms(..) )+import Text.Show.Pretty ( ppShow, ppShowList ) -- fromOthers (changed)+-- for the tests+import Uniform.Strings.Conversion hiding (S)+import Uniform.Strings.Infix hiding ((<.>), (</>))+import Uniform.Strings.Utilities+-- import Uniform.Zero+
+ Uniform/Strings/Conversion.hs view
@@ -0,0 +1,362 @@+---------------------------------------------------------------------+--+-- Module : Strings+-- Copyright :+--+-- | a module to convert between character string encodings+--++-- would require systematic checks what are permited characters+-- (especially for input to urlEncoding)++-- the latin encoding is produced by show ...+-- t2u is nearly invertible...++-- strings remain here, to be used when constructing the wrappers for+-- functions used from other packages (with String interfaces)+----------------------------------------------------------------------+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving+ , DeriveGeneric+ , DeriveAnyClass+ , TypeSynonymInstances+ #-}+{-# OPTIONS_GHC -fno-warn-missing-methods #-}+{-# OPTIONS_GHC -fno-warn-missing-signatures #-}+{-# OPTIONS_GHC -w #-}++module Uniform.Strings.Conversion (+ ByteString, LazyByteString+ , s2b, b2s, b2t, t2b, t2u, s2u+ , s2t, t2s+ , t2tl, tl2t+ -- uses UTF8 as encoding in ByteString+ -- urlencode is always represented the same as the input+ , Text (..), BSUTF (..), URL (..), URLform++ , b2bu, bu2b, bu2s, bu2t, t2bu, s2bu+ , u2b, u2t, b2uf, u2s, b2u+ , b2bl, bl2b -- lazy bytestring+ , bl2t, t2bl+ , bb2t, bb2s -- conversion with error if not UTF8+ , s2latin, t2latin, latin2t, latin2s -- conversion to the latin1 encoding+ , BSlat (..), s2lat, lat2s, t2lat, lat2t+ , s3lat, t3lat, s3latin, t3latin+ , s2url, url2s, b2urlf, urlf2b, unURL, t22latin+ , convertLatin, findNonLatinChars, findNonLatinCharsT+ , filterLatin+ , module Safe+ ) where+-- +import Safe (fromJustNote)+import GHC.Generics (Generic)+import Uniform.Zero (Zeros(zero) )++import Control.Monad (join)++import Data.Text (Text)+import qualified Data.Text as T+import Data.Char (ord)+import Data.List (nub)+import Data.ByteString (ByteString)+import qualified Data.ByteString as ByteString+import qualified Data.ByteString.Lazy as Lazy +import Data.ByteString.Char8 (pack, unpack)++import Data.Text.Encoding (decodeUtf8, decodeUtf8', encodeUtf8)++import qualified Network.URI as URI+import qualified Snap.Core as SN++import qualified Data.Text.Lazy as LText -- (toStrict, fromStrict)++bl2t :: LazyByteString ->Text+-- ^ conversion from LazyByteString to text (only if guarantee that only utf8 values)+bl2t = bu2t . BSUTF . bl2b++t2bl :: Text -> LazyByteString+t2bl = b2bl . t2b+++s2t :: String -> Text+-- ^ String to Text (invertable)+s2t = T.pack++t2s :: Text -> String+-- ^ String to Text (invertable)+t2s = T.unpack++tl2t :: LText.Text -> Text+tl2t = LText.toStrict++t2tl :: Text -> LText.Text+t2tl = LText.fromStrict++type LazyByteString = Lazy.ByteString++instance Zeros ByteString where zero = t2b ""+instance Zeros LazyByteString where zero = b2bl zero++newtype BSUTF = BSUTF ByteString+ deriving (Show, Read, Eq, Ord, Generic, Zeros, Semigroup, Monoid)++unBSUTF :: BSUTF -> ByteString+unBSUTF (BSUTF a) = a+++t2bu :: Text -> BSUTF+-- ^ Text to Bytestring (invertable)+t2bu = BSUTF . encodeUtf8++bu2t :: BSUTF -> Text+-- ^ ByteString to Text -- inverse (not an arbitrary input)+bu2t = decodeUtf8 . unBSUTF+++-- conversion ByteString BSUTF+b2bu :: ByteString -> Maybe BSUTF+b2bu a = if testByteStringUtf8 a then Just (BSUTF a) else Nothing++bu2b :: BSUTF -> ByteString+bu2b = unBSUTF++bu2s :: BSUTF -> String+bu2s = t2s . bu2t++testByteStringUtf8 :: ByteString -> Bool+-- ^ test whether a byte string is valid utf8 encoded+-- used for avoiding problems with the quickcheck conversions+testByteStringUtf8 b =+ case decodeUtf8' b of+ -- :: ByteString -> Either UnicodeException Text+ Left s -> False+ Right t -> True++t2b :: Text -> ByteString+t2b = bu2b . t2bu++b2t :: ByteString -> Maybe Text+b2t = fmap bu2t . b2bu++bb2s :: ByteString -> String+-- converts and stops with error when not UTF8+bb2s s = fromJustNote ("bb2s - bytestring to string conversion: " ++ show s+ ++ " was not a utf8") . b2s $ s++bb2t :: ByteString -> Text+-- converts and stopw with error when not UTF8+bb2t s = fromJustNote ("bb2s - bytestring to text conversion: " ++ show s+ ++ " was not a utf8") . b2t $ s+-- bytestring -- string (just a composition of t2s . b2t and reverse)+s2bu :: String -> BSUTF+-- ^ String to Bytestring (invertable)+s2bu = BSUTF . encodeUtf8 . s2t++--bu2s :: BSUTF -> String+---- ^ ByteString to String -- not inverse (not any arbitrary input)+--bu2s = t2s . decodeUtf8 . unBSUTF++s2b :: String -> ByteString+s2b = t2b . s2t++b2bl :: ByteString -> Lazy.ByteString+b2bl = Lazy.fromStrict++bl2b :: Lazy.ByteString -> ByteString+bl2b = Lazy.toStrict++b2s :: ByteString -> Maybe String+b2s = fmap t2s . b2t+++newtype URL = URL String deriving (Show, Eq)+instance Zeros URL where zero = URL zero++unURL :: URL -> String+unURL (URL t) = t+++s2url :: String -> URL+-- ^ convert string to url (uses code from Network.HTTP, which converts space into %20)+s2url = URL . URI.escapeURIString URI.isUnescapedInURIComponent+--s2url = URL . HTTP.urlEncode++url2s :: URL -> String+-- ^ convert url to string (uses code from Network.HTTP, which converts space into %20)+url2s = URI.unEscapeString . unURL++testUrlEncodingURI :: String -> Bool+testUrlEncodingURI a = a == (unURL . s2url . url2s . URL $ a)++url2u :: URL -> String+url2u = unURL+u2url :: String -> Maybe URL+u2url a = if testUrlEncodingURI a then Just (URL a) else Nothing+++s2u :: String -> String+-- ^ convert string to url (uses code from Network.HTTP, which converts space into %20)+s2u = url2u . s2url++u2s :: String -> Maybe String --not inverse+-- ^ convert url to string (uses code from Network.HTTP, which converts space into %20)+u2s = fmap url2s . u2url+++-- case for encoding of form content (with + for space)++newtype URLform = URLform ByteString deriving (Show, Eq)+unURLform :: URLform -> ByteString+unURLform (URLform t) = t+++b2urlf :: ByteString -> URLform+-- ^ convert string to url (uses code from SNAP, which converts space into +)+b2urlf = URLform . SN.urlEncode++urlf2b :: URLform -> ByteString+-- ^ convert url to string (uses code from SNAP, which converts space into +)+urlf2b = fromJustNote "urlf2b nothing" . SN.urlDecode . unURLform+++testUrlEncodingSNAP :: ByteString -> Bool+testUrlEncodingSNAP a = maybe False ((a ==). SN.urlEncode) . SN.urlDecode $ a+ +urlf2u :: URLform -> ByteString+urlf2u = unURLform+u2urlf :: ByteString -> Maybe URLform+u2urlf a = if testUrlEncodingSNAP a then Just (URLform a) else Nothing+-- this test allows control in url encoded strings ...+++b2uf :: ByteString -> ByteString+-- ^ convert ByteString to url (uses code from SNAP which converts space into +)+b2uf = urlf2u . b2urlf++uf2b :: ByteString -> Maybe ByteString --not inverse+-- ^ convert url to ByteString (uses code from SNAP, which converts space into +)+uf2b = fmap urlf2b . u2urlf+++t2u :: Text -> Text+t2u = s2t . s2u . t2s+u2t :: Text -> Maybe Text+u2t = fmap s2t . u2s . t2s++b2u :: ByteString -> Maybe ByteString+b2u = fmap (s2b . s2u) . b2s+u2b :: ByteString -> Maybe ByteString+u2b = fmap s2b . join . fmap u2s . b2s+++-- | bytestring with latin1 encoded characters+newtype BSlat = BSlat ByteString deriving (Show, Eq)+unBSlat :: BSlat -> ByteString+unBSlat (BSlat a) = a+++lat2s :: BSlat -> String+-- ^ bytestring with latin encoding to string+lat2s = latin2s . unBSlat++s2lat :: String -> Maybe BSlat -- is this always possible ?+-- ^ string encoded as ByteString with latin encoding, if possible+s2lat = fmap BSlat . s22latin++s3lat :: String -> BSlat -- is this always possible ?+-- ^ string converted to represenatable as latin and then encoded+-- lossy!+s3lat = BSlat . s3latin++lat2t :: BSlat -> Text+-- ^ Text encoded as ByteString with latin encoding, if possible+lat2t = latin2t . unBSlat++t2lat :: Text -> Maybe BSlat -- is this always possible+-- ^ Text encoded as ByteString with latin encoding, if possible+t2lat = fmap BSlat . t22latin++t3lat :: Text -> BSlat -- is this always possible+-- ^ Text converted to represenatable as latin and then encoded+-- lossy!+t3lat = BSlat . t3latin++latin2s :: ByteString -> String+ -- | works always, but produces unexpected results if bytestring is not latin encoded+latin2s = Data.ByteString.Char8.unpack+--+s2latin :: String -> ByteString+ -- | works always, but produces unexpected results if bytestring is not latin encoded+s2latin = Data.ByteString.Char8.pack++s22latin :: String -> Maybe ByteString+s22latin s = if all ((<256) . ord) s then Just . s2latin $ s else Nothing -- Data.ByteString.Char8.pack . T.unpack++s3latin :: String -> ByteString+s3latin = s2latin . convertLatin++filterLatin :: String -> String+filterLatin = filter ((<256).ord )++convertLatin :: String -> String+-- ^ convert a string to contain only characters in latin1+convertLatin = map conv2latinChar++conv2latinChar :: Char -> Char+-- ^ convert character not in the latin1 encoding (intelligently treating quotes and double quotes)+-- possibly other cases later added+conv2latinChar c = if ord c < 256 then c else+ case c of+ '\x201C' -> '"'+ '\x201D' -> '"'+ '\x201E' -> '"'+ '\8212' -> '-' -- em dash+ '\8222' -> '"' -- lower quote+ '\8216' -> '\'' -- left single quote+ '\8217' -> '\'' -- right single quote+ '\8218' -> '\'' -- quote+ '\8221' -> '"' -- unclear why 8221 but is quote+-- '\x2018' -> '\'' -- same as 8216+-- '\x2019' -> '\'' -- same as 8217++ _ -> c -- '\SUB' -- could be another char ? \SUB++findNonLatinChars :: String -> String+-- ^ the result is a string of all the characters not in the latin1 encoding+-- possibly apply conv2latinChar first+findNonLatinChars = nub . filter ((>256).ord )+-- (\c -> conv2latinChar c == '\SUB')++findNonLatinCharsT :: Text -> Text+-- ^ the result is a string of all the characters not in the latin1 encoding+findNonLatinCharsT = s2t . findNonLatinChars . t2s++++latin2t :: ByteString -> Text+latin2t = s2t . latin2s -- T.pack . Data.ByteString.Char8.unpack++t2latin :: Text -> ByteString+-- text to bytestring - works always, but produces unexpected results if bytestring is not latin encoded+t2latin = s2latin . t2s -- Data.ByteString.Char8.pack . T.unpack++t22latin :: Text -> Maybe ByteString+-- ^ converts text to bytestring, if meaningful+t22latin = s22latin . t2s -- Data.ByteString.Char8.pack . T.unpack+--t22latin t = if all ((<256) . ord) (t2s t) then Just . s2latin . t2s $ t else Nothing -- Data.ByteString.Char8.pack . T.unpack++t3latin :: Text -> ByteString+-- text to bytestring - meaningful, but converted -- lossy!+t3latin = s3latin . t2s -- Data.ByteString.Char8.pack . T.unpack+--++putIOwords :: [Text] -> IO ()+putIOwords = putStrLn . unlines . map t2s++-- chars :: [GHC.Word.Word8]+chars = [198, 216, 197, 206, 219,140,252,202, 419, 420, 1937 ]+difficultBString = ByteString.pack chars+difficultTString = "\198\216\197\206\219\140\252\202\419\420\1937"+
+ Uniform/Strings/Infix.hs view
@@ -0,0 +1,67 @@+-----------------------------------------------------------------------------+--+-- Module : StringInfix++-- infix operations with <X> to insert X in between - unconditional+-- even if the two strings are empty+-----------------------------------------------------------------------------+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS_GHC -fno-warn-missing-methods #-}+{-# OPTIONS_GHC -w #-}++module Uniform.Strings.Infix where++import GHC.Exts (IsString (..))+import Uniform.Strings.Conversion ( Text )+import Uniform.Strings.Utilities ( CharChains(append) )+++(<:>) :: Text -> Text -> Text+-- ^ append text with colon in between+a <:> b = a <> ":" <> b++--(<+>) :: (IsString s, CharChains s) => s -> s -> s+(<+>) :: Text -> Text -> Text+-- append text with plus in between+a <+> b = a <> "+" <> b++--(<->) :: (IsString s, CharChains s) => s -> s -> s+(<->) :: Text -> Text -> Text+-- ^ append text with dash in between+a <-> b = a <> "-" <> b++--(</>) :: (IsString s, CharChains s) => s -> s -> s+(</>) :: Text -> Text -> Text+-- ^ append text with slash in between+a </> b = a <> "/" <> b++(<.>) :: Text -> Text -> Text+a <.> b = a <> "." <> b+-- ^ append text with dot in between++(<#>) :: Text -> Text -> Text+-- append text with hash in between+a <#> b = a <> "#" <> b++(<|>) :: Text -> Text -> Text+-- ^ append text with blank in between, (a character does not work as name)+-- possible conflict with parsec+a <|> x = a <> " " <> x++wrapInSpitz :: Text -> Text+-- ^ insert text in <..>+wrapInSpitz a = "<" <> a <> ">"++wrapInDoubleQuotes :: Text -> Text+-- ^ insert text in <..>+wrapInDoubleQuotes a = "\"" <> a <> "\""++wrapInBraces :: Text -> Text+-- ^ insert text in <..>+wrapInBraces a = "{" <> a <> "}"++addXatEnd :: (IsString s, CharChains s) => s -> s -> s+addXatEnd = flip append
+ Uniform/Strings/Utilities.hs view
@@ -0,0 +1,458 @@+ --------------------------------------------------------------------+--+-- Module : Strings+-- Copyright :+--+-- | a module with a class for strings, such that the normal functions are+-- all polymorphic for string and text (and total)+-- the string (i.e. [Char]) functions are the semantic definitions,+-- the other implementation are tested against these.+-- except intercalate, which returns Maybe+-- (the corresponding restrictions for the unlines and unwords functions are not enforced)+--+-- performance can be improved by using the "native" functions+-- could be expanded++-- class niceStrings can be replaced or integrated in the generic strings+-- it may be useful to have more than one show like operation+----------------------------------------------------------------------+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE PackageImports #-}++{-# OPTIONS_GHC -fno-warn-missing-methods #-}+{-# OPTIONS_GHC -w #-}++module Uniform.Strings.Utilities+ (module Uniform.Zero+ , module Uniform.ListForm+ , CharChains (..)+ , CharChains2 (..)+ , NiceStrings (..)+ , unlinesT, unwordsT+ , wordsT, concatT, showT, readNoteT, readNoteTs+ , sortCaseInsensitive, cmpCaseInsensitive+ , maybe2string+ , showList'+ , putIOwords, debugPrint + , T.toTitle+ , toLowerStart, toUpperStart + , prop_filterChar+ , isSpace, isLower+ , PrettyStrings (..)+ -- to generalize+ , dropWhile, takeWhile, span, break+ , formatInt+ )+ where++import Uniform.Zero (Zeros(..))+import Uniform.ListForm ( ListForms(..) )+++import Data.Char (isSpace, isLower, toLower, toUpper)+import Text.Printf (PrintfArg, PrintfType, printf)++import Data.List as L+ ( sortBy, intercalate, isInfixOf, isPrefixOf, nub, stripPrefix )+import GHC.Exts (IsString (..))++import qualified Data.List.Split as S (splitOn)+import Data.Maybe ( catMaybes )+-- import Data.Monoid+import Data.Text (Text)+import qualified Data.Text as T (head, cons, tail, append, singleton, unwords, words, unlines, lines, empty, toUpper, toLower, concat, isPrefixOf, isInfixOf, stripPrefix, stripSuffix, intercalate, splitOn, strip, dropEnd, reverse, length, filter, take, drop, replace, null, toTitle)+import qualified Data.List.Utils as LU (replace)+import Safe ( readNote )+import Uniform.Strings.Conversion+ ( Text,+ BSUTF,+ LazyByteString,+ s2t,+ t2s,+ t2bu,+ bu2t,+ bu2s,+ t2b,+ s2bu,+ b2bl )+import qualified Data.ByteString.Lazy as Lazy (append, length, take, drop)+import Text.Read (readMaybe)+import Text.Show.Pretty ( ppShow ) +import "monads-tf" Control.Monad.State (MonadIO, liftIO)+import Control.Monad (when)+++readNoteTs :: (Show a, Read a) => [Text] -> Text -> a -- TODO+-- ^ read a Text into a specific format+readNoteTs msg a = readNote (unlines (map t2s msg) <> show a) . t2s $ a++readNoteT :: Read a => Text -> Text -> a -- TODO+-- ^ read a Text into a specific format+readNoteT msg = readNote (t2s msg) . t2s++showList' :: Show a => [a] -> Text+-- ^ show a collection of lines+showList' = unlines' . map showT++toLowerStart :: Text -> Text+-- ^ convert the first character to lowercase - for Properties in RDF+toLowerStart t = (toLower . T.head $ t) `T.cons` T.tail t++toUpperStart :: Text -> Text+-- ^ convert the first character to Uppercase - for PosTags in Spanish+toUpperStart t = (toUpper . T.head $ t) `T.cons` T.tail t++dropLast :: Int -> [a] -> [a]+dropLast n = reverse . drop n . reverse++putIOwords :: MonadIO m => [Text] -> m ()+putIOwords = liftIO . putStrLn . t2s . unwords'++debugPrint :: (MonadIO m) => Bool -> [Text] -> m ()+-- ^ print the texts when the bool is true (flag debug)+debugPrint flag texts = when flag $ putIOwords texts +++--instance Zeros String where zero = (""::String)+instance Zeros Text where zero = "" :: Text++instance ListForms Text where+ type LF Text = Char+ appendTwo = T.append+ mkOne = T.singleton++instance ListForms String where+ type LF String = Char+ appendTwo = (++)+ mkOne = show++instance ListForms LazyByteString where+ type LF LazyByteString = Char+-- appendTwo = Lazy.append+ mkOne = b2bl . t2b . T.singleton++instance ListForms BSUTF where+ type LF BSUTF = Char+-- appendTwo a b = t2bu . appendTwo (bu2t a) $ bu2t b+ mkOne = t2bu . T.singleton++--instance Zeros BSUTF where -- derived in Conversion+-- zero = t2bu ""++class (Zeros a, ListForms a, Eq a) => CharChains a where+-- {-# MINIMAL #-}++ toString :: a -> String+ toText :: Show a => a -> Text+ -- ^ conversion+++ unwords' :: [a] -> a+ words' :: a -> [a]+ unlines' :: [a] -> a+ lines' :: a -> [a]+-- punwords :: [a] -> s+ toText = s2t . show+ append', append :: a -> a -> a -- duplication?+ append = appendTwo+ append' = appendTwo++ null' :: a -> Bool+ null' = isZero+ mknull :: a+ toLower' :: a -> a+ -- ^ convert the string to lowercase, idempotent+ -- is not inverse of toUpper+ toUpper':: a -> a+ -- ^ is not idempotent and gives different results for string and text (sz and similar ligatures)++ isPrefixOf', isInfixOf', isPostfixOf' :: a -> a -> Bool+ isPostfixOf' a = isPrefixOf' (reverseString a) . reverseString+ stripPrefix' :: a -> a -> Maybe a+ -- ^ takes the prefix away, if present (and return rest). nothing if no prefix+ stripSuffix' :: a -> a -> Maybe a+ concat' :: [a] -> a+ trim' :: a -> a+ -- ^ removes all spaces front and back, idempotent+ reverseString, reverse' :: a -> a+ reverse' = reverseString+ removeLast :: a -> a+ -- ^ remove last char+ removeChar ::Char -> a -> a+ -- ^ remove a character from a string+ filterChar:: (Char -> Bool) -> a -> a+ -- filter lets pass what is true+ lengthChar :: a -> Int+ nubChar :: a -> a+ drop' :: Int -> a -> a+ -- drop n char from input start+ take' :: Int -> a -> a+ -- ^ add a splitAt or dropN function+ intercalate' :: a -> [a] -> Maybe a+ -- ^ splitOn' and intercalate' are inverses (see Data.SplitList)+ -- returns Nothing if second is empty and intercalate "x" "" gives Just ""+ -- return Nothing if first is empty or contained in second to achievee inverse with splitOn+ splitOn' :: a -> a -> Maybe [a]+ -- ^ splits the first by all occurences of the second + -- the second is removed from results+ -- returns Nothing if second is empty++ printf' :: (PrintfArg r) => String -> r -> a+ -- ^ formats a string accoding to a pattern - restricted to a single string (perhaps)+ -- requires type of argument fixed!++-- length' :: a -> Int+ replace' :: a -> a -> a -> a+ -- replace the first string with the second string in the third string+ readMaybe' :: Read b => a -> Maybe b+ -- read something... needs type hints++ prop_filterChar :: a -> Bool+ -- test with fixed set of chars to filter out++class CharChains2 x a where+ show' :: x -> a+-- replaced with toString or toText++instance CharChains2 Int String where+ show' = show+instance CharChains2 Bool String where+ show' = show+instance CharChains2 () String where+ show' = show+instance CharChains2 Int Text where+ show' = s2t . show+instance CharChains2 Bool Text where+ show' = s2t . show+instance CharChains2 () Text where+ show' = s2t . show++instance CharChains2 Float String where+ show' = show+instance CharChains2 Float Text where+ show' = s2t . show++instance CharChains2 Double String where+ show' = show+instance CharChains2 Double Text where+ show' = s2t . show+instance CharChains2 Text Text where+ -- avoid the "" surrounding show text+ show' = id+instance CharChains2 String Text where+ show' = s2t+instance CharChains2 Text String where+ -- avoid the "" surrounding show text+ show' = t2s+instance CharChains2 String String where+ show' = id++instance (Show a, Show b) => CharChains2 (a,b) String where+ show' (a,b) = show (a,b)+instance (Show a, Show b) => CharChains2 (a,b) Text where+ show' (a,b) = s2t $ show (a,b)++instance (Show a) => CharChains2 [a] String where+ show' s = show s+instance (Show a) => CharChains2 [a] Text where+ show' s = s2t . show $ s++instance CharChains String where+ toString = id+ toText = s2t++ unwords' = unwords+ words' = words+ unlines' = unlines+ lines' = lines+ null' = null+ mknull = ""+ toUpper' = map toUpper+ toLower' = map toLower+ concat' = concat+ isPrefixOf' = isPrefixOf+ isInfixOf' = isInfixOf+ stripPrefix' = stripPrefix+ stripSuffix' a = fmap reverse . stripPrefix (reverse a) . reverse++ intercalate' s a+ | null a = Nothing+ | null s = Nothing+ | s `isInfixOf` concat' a = Nothing+ | otherwise = Just $ L.intercalate s a++ splitOn' o s+ | null' o = Just []+ | null' s = Just [""]+ | otherwise = Just $ S.splitOn o s+ trim' = f . f+ where f = reverse . dropWhile isSpace+ removeLast a = if null' a+ then mknull+ else reverseString . tail . reverseString $ a+ reverseString = reverse+ printf' = printf+ lengthChar = length+ removeChar c = filter (c /=)+ filterChar = filter++ nubChar = nub+ take' = take+ drop' = drop+ replace' = LU.replace+ readMaybe' = readMaybe++instance CharChains Text where+ toString = t2s+ toText = id++ unwords' = T.unwords+ words' = T.words+ lines' = T.lines+ unlines' = T.unlines+ mknull = T.empty+ toUpper' = T.toUpper+ toLower' = T.toLower+ concat' = T.concat+ isPrefixOf' = T.isPrefixOf+ isInfixOf' = T.isInfixOf+ stripPrefix' = T.stripPrefix+ stripSuffix' = T.stripSuffix++ intercalate' s a+ | null a = Nothing :: Maybe Text+ | null' s = Nothing+ | s `isInfixOf'` concat' a = Nothing+ | otherwise = Just $ T.intercalate s a++ splitOn' o s+ | null' o = Just []+ | null' s = Just [""]+ | otherwise = Just $ T.splitOn o s++ trim' = T.strip + removeLast a = T.dropEnd 1 a++ reverseString = T.reverse ++ printf' p = s2t . printf p+ lengthChar = T.length+ removeChar c = T.filter (c /=)+ filterChar = T.filter+ nubChar = s2t . nub .t2s+ take' = T.take+ drop' = T.drop++ prop_filterChar a = t2s af == (filterChar cond . t2s $ a)+ where+ cond x = x `notElem` ['a', '\r', '1']+ af = filterChar cond a :: Text+ replace' = T.replace+ readMaybe' = readMaybe' . t2s++instance CharChains LazyByteString where+ append' = Lazy.append+ lengthChar a = fromIntegral . Lazy.length $ a + -- gives not exact value??+ take' = Lazy.take . fromIntegral + drop' = Lazy.drop . fromIntegral ++unwordsT :: [Text] -> Text+unwordsT = T.unwords +-- ^ to fix types for overloaded strings++wordsT :: Text -> [Text]+wordsT = words'++concatT :: [Text] -> Text+concatT = concat'++showT t = s2t c+ where c = show t :: String++instance CharChains BSUTF where+-- works on utf8 encoded bytestring, convert with b2bu and bu2b++ toString = bu2s+ toText = bu2t++ unwords' = t2bu . unwords' . map bu2t+ words' = map t2bu . words' . bu2t+ lines' = map t2bu . lines' . bu2t+ unlines' = t2bu . unlines' . map bu2t++-- append' a b = t2bu . append' (bu2t a) $ bu2t b+ null' = T.null . bu2t+ toUpper' = t2bu . toUpper' . bu2t+ toLower' = t2bu . toLower' . bu2t+ concat' = t2bu . concat' . map bu2t+ isPrefixOf' t s = isPrefixOf' (bu2t s) (bu2t t)+ isInfixOf' t s = isInfixOf' (bu2t s) (bu2t t)+ stripPrefix' p s = t2bu <$> stripPrefix' (bu2t p) (bu2t s)+ intercalate' x a = fmap t2bu (intercalate' (bu2t x) (map bu2t a))+ splitOn' o s = fmap t2bu <$> splitOn' (bu2t o) (bu2t s)+ trim' = s2bu . trim' . bu2s++formatInt :: Int -> Int -> Text+formatInt n = s2t . case n of+ 6 -> printf ['%', '0', '6', 'd']+ 5 -> printf ['%', '0', '5', 'd']+ 3 -> printf ['%', '0', '3', 'd']+ 2 -> printf ['%', '0', '2', 'd']+ a -> error ("formatInt not expected int" <> show a)++unlinesT :: [Text] -> Text+unlinesT = unlines'++sortCaseInsensitive :: (Ord a, CharChains a) => [a] -> [a]+sortCaseInsensitive = sortBy cmpCaseInsensitive++cmpCaseInsensitive :: (Ord a, CharChains a) => a -> a -> Ordering+cmpCaseInsensitive s1 s2 = compare ( toLower' s1) ( toLower' s2)++maybe2string :: (IsString s) => Maybe s -> s+maybe2string Nothing = "" -- TODO+maybe2string (Just s) = s++string2maybe :: (Eq a, IsString a) => a -> Maybe a+string2maybe x = if x == "" then Nothing else Just x++class NiceStrings a where+ shownice, showNice :: a -> Text+ showNice = shownice+ shownice = showNice + showlong :: a -> Text+ showlong = shownice -- a default+class Show a => PrettyStrings a where + showPretty :: a -> Text+instance Show a => PrettyStrings a where+ showPretty = s2t . ppShow++instance NiceStrings Text where+ shownice = id+ showlong = id++instance NiceStrings Int where+ shownice = show'+ showlong = show'+instance NiceStrings Double where+ shownice = show'+ showlong = show'++instance (NiceStrings a, NiceStrings b) => NiceStrings (a,b) where+ shownice (a,b) = unwords' [shownice a, shownice b]+ showlong (a,b) = unwords' [showlong a, showlong b]+instance (NiceStrings a) => NiceStrings [a] where+ shownice as = concat' . catMaybes $ [intercalate' "," . map shownice $ as, Just "\n"]+ shownice as = concat' . catMaybes $ [intercalate' "," . map showlong $ as, Just "\n"]++instance (NiceStrings a) => NiceStrings (Maybe a) where+ shownice (Just a) = shownice a+ shownice Nothing = "Nothing"+
+ uniform-strings.cabal view
@@ -0,0 +1,64 @@+cabal-version: 2.2++-- This file has been generated from package.yaml by hpack version 0.34.2.+--+-- see: https://github.com/sol/hpack++name: uniform-strings+version: 0.1.0+synopsis: Manipulate and convert strings of characters uniformly and consistently+description: Reduce complexity of Haskell by providing a + .+ - set of uniformly named conversion functions between + .+ - String, + .+ - Text, + .+ - ByteString, + .+ - LazyByteString and + .+ - urlEncoding+ .+ with the most important text manipulation functions for string and text+ where the semantic is the same (tested with quickcheck).+ Tentatively some infix string manipulations are offered as well. + .+ Please see the README on GitHub at <https://github.com/andrewufrank/uniform-strings/readme>+category: Data Text Uniform+bug-reports: https://github.com/andrewufrank/uniform-strings/issues+author: Andrew Frank+maintainer: Andrew U. Frank <uniform@gerastree.at>+copyright: 2021 Andrew U. Frank+license: GPL-2.0-only+build-type: Simple+extra-source-files:+ README.md+ ChangeLog.md++library+ exposed-modules:+ Uniform.Strings+ Uniform.Strings.Conversion+ Uniform.Strings.Infix+ Uniform.Strings.Utilities+ other-modules:+ Paths_uniform_strings+ hs-source-dirs:+ ./.+ build-depends:+ MissingH+ , base >=4.7 && <5+ , bytestring+ , monads-tf+ , network-uri+ , pretty-show+ , safe+ , snap-core+ , split+ , text+ , text-icu+ , uniform-algebras+ default-language: Haskell2010+ autogen-modules: Paths_uniform_strings