-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Encode and decode separated values (CSV, PSV, ...) -- -- -- sv (separated values) is a library for parsing, decoding, encoding, -- and printing CSV and similar formats (such as PSV, TSV, and many -- more). -- -- sv uses an Applicative combinator style for decoding and encoding, -- rather than a type class based approach. This means we can have -- multiple decoders for the same type, multiple combinators of the same -- type, and we never have to worry about orphan instances. These -- decoders can be stiched together from provided primitives and -- combinators, or you can build one from a parser from your favourite -- parser combinator library. -- -- sv returns values for all errors that occur - not just the first. -- Errors have more structure than just a string, indicating what went -- wrong. -- -- sv's parser is exposed so you can use it independently of the -- decoding, and encoding and printing are similarly standalone. -- -- sv focuses on correctness, on flexible and composable data types, and -- on useful and informative error values. Speed is also important to us, -- but it is not as important as these other qualities. -- -- sv tries not to be opinionated about how your data should look. We -- intend for the user to have a great degree of freedom to build the -- right decoder for their dataset. -- -- Parts of sv are intended to be imported as follows: -- --
--   import Data.Sv
--   import qualified Data.Sv.Decode as D
--   import qualified Data.Sv.Encode as E
--   
-- -- Examples: -- -- @package sv @version 0.1 module Data.Vector.NonEmpty -- | A non-empty value of Vector data NonEmptyVector a NonEmptyVector :: a -> (Vector a) -> NonEmptyVector a -- | Convert a NonEmpty list to a NonEmptyVector fromNel :: NonEmpty a -> NonEmptyVector a -- | Convert a NonEmptyVector to a NonEmpty list toNel :: NonEmptyVector a -> NonEmpty a -- | Get or set the head of a NonEmptyVector headNev :: Lens' (NonEmptyVector a) a -- | Get or set the head of a NonEmptyVector tailNev :: Lens' (NonEmptyVector a) (Vector a) instance GHC.Generics.Generic (Data.Vector.NonEmpty.NonEmptyVector a) instance GHC.Show.Show a => GHC.Show.Show (Data.Vector.NonEmpty.NonEmptyVector a) instance GHC.Classes.Ord a => GHC.Classes.Ord (Data.Vector.NonEmpty.NonEmptyVector a) instance GHC.Classes.Eq a => GHC.Classes.Eq (Data.Vector.NonEmpty.NonEmptyVector a) instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Data.Vector.NonEmpty.NonEmptyVector a) instance GHC.Base.Functor Data.Vector.NonEmpty.NonEmptyVector instance Data.Functor.Bind.Class.Apply Data.Vector.NonEmpty.NonEmptyVector instance GHC.Base.Applicative Data.Vector.NonEmpty.NonEmptyVector instance Data.Foldable.Foldable Data.Vector.NonEmpty.NonEmptyVector instance Data.Semigroup.Foldable.Class.Foldable1 Data.Vector.NonEmpty.NonEmptyVector instance Data.Traversable.Traversable Data.Vector.NonEmpty.NonEmptyVector instance Data.Semigroup.Traversable.Class.Traversable1 Data.Vector.NonEmpty.NonEmptyVector instance Data.Semigroup.Semigroup (Data.Vector.NonEmpty.NonEmptyVector a) -- | Quote characters can be escaped in CSV documents by using two quote -- characters instead of one. sv's parser will unescape these sequences -- as it parses them, so it wraps them in the newtype Unescaped -- -- Encoding requires you to provide an Escaper, which is a -- function to escape strings on the way out. module Text.Escape -- | Wrapper for text that is known to be in an unescaped form newtype Unescaped a Unescaped :: a -> Unescaped a [getRawUnescaped] :: Unescaped a -> a -- | A function that, given a char, escapes all occurrences of that char. -- -- This version allows the escaping to be type-changing. For example, -- escaping a single char can result in a string with two characters. type Escaper s t = Char -> Unescaped s -> t -- | A function that, given a char, escapes all occurrences of that char. type Escaper' a = Char -> Unescaped a -> a -- | Replaces all occurrences of the given character with two occurrences -- of that character, non-recursively, in the given String. -- --
--   >>> escapeString ''' "hello 'string'"
--   "hello ''string''"
--   
escapeString :: Escaper' String -- | Replaces all occurrences of the given character with two occurrences -- of that character in the given Text -- --
--   {- LANGUAGE OverloadedStrings -}
--   
--   >>> escapeText ''' "hello text"
--   "hello 'text'"
--   
escapeText :: Escaper' Text -- | Replaces all occurrences of the given character with two occurrences -- of that character in the given ByteString, which is assumed to be -- UTF-8 compatible. -- --
--   {- LANGUAGE OverloadedStrings -}
--   >>> escapeUtf8 ''' "hello bytestring"
--   "hello 'bytestring'"
--   
escapeUtf8 :: Escaper' ByteString -- | Replaces all occurrences of the given character with two occurrences -- of that character in the given lazy ByteString, which is assumed to be -- UTF-8 compatible. -- --
--   {- LANGUAGE OverloadedStrings -}
--   
--   >>> escapeUtf8Lazy ''' "hello 'lazy bytestring'"
--   "hello ''lazy bytestring''"
--   
escapeUtf8Lazy :: Escaper' ByteString -- | Escape a character, which must return a string. -- --
--   >>> escapeChar ''' '''
--   "''"
--   
-- --
--   >>> escapeChar ''' 'z'
--   "z"
--   
escapeChar :: Escaper Char String instance GHC.Generics.Generic (Text.Escape.Unescaped a) instance Data.Traversable.Traversable Text.Escape.Unescaped instance Data.Foldable.Foldable Text.Escape.Unescaped instance GHC.Base.Functor Text.Escape.Unescaped instance GHC.Base.Monoid a => GHC.Base.Monoid (Text.Escape.Unescaped a) instance Data.Semigroup.Semigroup a => Data.Semigroup.Semigroup (Text.Escape.Unescaped a) instance GHC.Show.Show a => GHC.Show.Show (Text.Escape.Unescaped a) instance GHC.Classes.Ord a => GHC.Classes.Ord (Text.Escape.Unescaped a) instance GHC.Classes.Eq a => GHC.Classes.Eq (Text.Escape.Unescaped a) instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Text.Escape.Unescaped a) module Data.Sv.Print.Options -- | Options to configure the printing process data PrintOptions a PrintOptions :: (a -> Builder) -> Escaper' a -> PrintOptions a -- | How do I convert these values into ByteString Builders? This -- depends not only on type, but on character encoding. Default: -- utf8PrintOptions [_build] :: PrintOptions a -> a -> Builder -- | How do I escape quotes which appear in this value? Default: -- escapeUtf8 [_escape] :: PrintOptions a -> Escaper' a -- | Classy optics for PrintOptions class HasPrintOptions c a | c -> a printOptions :: HasPrintOptions c a => Lens' c (PrintOptions a) build :: HasPrintOptions c a => Lens' c (a -> Builder) escape :: HasPrintOptions c a => Lens' c (Escaper' a) -- | Print options for Svs containing UTF-8 bytestrings defaultPrintOptions :: PrintOptions ByteString -- | Print options for Svs containing UTF-8 bytestrings utf8PrintOptions :: PrintOptions ByteString -- | Print options for Svs containing UTF-8 lazy bytestrings utf8LazyPrintOptions :: PrintOptions ByteString -- | Print options for Svs containing Text textPrintOptions :: PrintOptions Text -- | Print options for Svs containing Strings stringPrintOptions :: PrintOptions String instance Data.Sv.Print.Options.HasPrintOptions (Data.Sv.Print.Options.PrintOptions a) a -- | A sum type for line endings module Text.Newline -- | Newline is a sum type for line endings data Newline -- |
--   "\r"
--   
CR :: Newline -- |
--   "\n"
--   
LF :: Newline -- |
--   "\rn"
--   
CRLF :: Newline -- | AsNewline is a classy prism for Newline class AsNewline r _Newline :: AsNewline r => Prism' r Newline _CR :: AsNewline r => Prism' r () _LF :: AsNewline r => Prism' r () _CRLF :: AsNewline r => Prism' r () -- | Convert a Newline to a String. Since this uses -- IsString, it works for other data types, like Text or -- ByteString. newlineToString :: IsString s => Newline -> s -- | Try to parse text into a Newline parseNewline :: Text -> Maybe Newline instance GHC.Show.Show Text.Newline.Newline instance GHC.Classes.Ord Text.Newline.Newline instance GHC.Classes.Eq Text.Newline.Newline instance Text.Newline.AsNewline Text.Newline.Newline instance Text.Newline.AsNewline Data.Text.Internal.Text instance Control.DeepSeq.NFData Text.Newline.Newline -- | A sum type for quote characters module Text.Quote -- | A sum type for quote characters. Either single or double quotes. data Quote SingleQuote :: Quote DoubleQuote :: Quote -- | Classy prisms for Quote class AsQuote r _Quote :: AsQuote r => Prism' r Quote _SingleQuote :: AsQuote r => Prism' r () _DoubleQuote :: AsQuote r => Prism' r () -- | Convert a Quote to the Char it represents. quoteChar :: Prism' Char Quote -- | Convert a Quote to a String. Since this uses -- IsString, it works for other data types, like Text or -- ByteString. quoteToString :: IsString a => Quote -> a instance GHC.Show.Show Text.Quote.Quote instance GHC.Classes.Ord Text.Quote.Quote instance GHC.Classes.Eq Text.Quote.Quote instance Text.Quote.AsQuote Text.Quote.Quote instance Text.Quote.AsQuote GHC.Types.Char instance Control.DeepSeq.NFData Text.Quote.Quote -- | A sum type for space characters module Text.Space -- | HorizontalSpace is a subset of Char. To move back and -- forth betwen it and Char, String, or Text, use -- _HorizontalSpace data HorizontalSpace Space :: HorizontalSpace Tab :: HorizontalSpace -- | Classy prisms for HorizontalSpaces class AsHorizontalSpace r _HorizontalSpace :: AsHorizontalSpace r => Prism' r HorizontalSpace _Space :: AsHorizontalSpace r => Prism' r () _Tab :: AsHorizontalSpace r => Prism' r () -- | Helpful alias for lists of Spaces type Spaces = Vector HorizontalSpace -- | One space single :: Spaces -- | As many spaces as you'd like manySpaces :: Int -> Spaces -- | One tab tab :: Spaces -- | Turn a Space into a Char. To go the other way, see -- charToSpace spaceToChar :: HorizontalSpace -> Char -- | Try to turn a Char into a Space. To go the other way, see -- spaceToChar charToSpace :: Char -> Maybe HorizontalSpace -- | Parse Text into Spaces, or turn spaces into Text spacesText :: Prism' Text Spaces -- | Parse String into Spaces, or convert Spaces into -- String spacesString :: Prism' String Spaces -- | Spaced is a value with zero or many horizontal spaces around it -- on both sides. data Spaced a Spaced :: Spaces -> Spaces -> a -> Spaced a [_before] :: Spaced a -> Spaces [_after] :: Spaced a -> Spaces [_value] :: Spaced a -> a -- | Classy lenses for Spaced class HasSpaced s t a b | s -> a, t -> b, s b -> t, t a -> s spaced :: HasSpaced s t a b => Lens s t (Spaced a) (Spaced b) after :: (HasSpaced s t a b, (s ~ t)) => Lens s t Spaces Spaces before :: (HasSpaced s t a b, (s ~ t)) => Lens s t Spaces Spaces spacedValue :: HasSpaced s t a b => Lens s t a b after :: (HasSpaced s t a b, s ~ t, a ~ b) => Lens s t Spaces Spaces before :: (HasSpaced s t a b, s ~ t, a ~ b) => Lens s t Spaces Spaces spacedValue :: (HasSpaced s t a b, s ~ t, a ~ b) => Lens s t a b -- | betwixt is just the constructor for Spaced with a -- different argument order, which is sometimes useful. betwixt :: Spaces -> a -> Spaces -> Spaced a -- | uniform puts the same spacing both before and after something. uniform :: Spaces -> a -> Spaced a -- | Places its argument in a Spaced with no spaces. unspaced :: a -> Spaced a -- | Remove spaces from the argument removeSpaces :: Spaced a -> Spaced a instance GHC.Generics.Generic (Text.Space.Spaced a) instance GHC.Show.Show a => GHC.Show.Show (Text.Space.Spaced a) instance GHC.Classes.Ord a => GHC.Classes.Ord (Text.Space.Spaced a) instance GHC.Classes.Eq a => GHC.Classes.Eq (Text.Space.Spaced a) instance GHC.Show.Show Text.Space.HorizontalSpace instance GHC.Classes.Ord Text.Space.HorizontalSpace instance GHC.Classes.Eq Text.Space.HorizontalSpace instance Text.Space.HasSpaced (Text.Space.Spaced a) (Text.Space.Spaced b) a b instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Text.Space.Spaced a) instance GHC.Base.Functor Text.Space.Spaced instance GHC.Base.Applicative Text.Space.Spaced instance Data.Foldable.Foldable Text.Space.Spaced instance Data.Traversable.Traversable Text.Space.Spaced instance Text.Space.AsHorizontalSpace Text.Space.HorizontalSpace instance Text.Space.AsHorizontalSpace GHC.Types.Char instance Control.DeepSeq.NFData Text.Space.HorizontalSpace module Data.Sv.Syntax.Field -- | A Field is a single cell from a CSV document. -- -- Its value is either Quoted, which indicates the type of quote -- surrounding the value, or it is Unquoted, containing only the -- value. data Field s Unquoted :: s -> Field s Quoted :: Quote -> (Unescaped s) -> Field s -- | Fields are often surrounded by spaces type SpacedField a = Spaced (Field a) -- | Spaced is a value with zero or many horizontal spaces around it -- on both sides. data Spaced a Spaced :: Spaces -> Spaces -> a -> Spaced a -- | Classy Traversal' for things containing Fields class HasFields c d s t | c -> s, d -> t, c t -> d, d s -> c fields :: HasFields c d s t => Traversal c d (Field s) (Field t) -- | Classy prisms for Field class (HasFields s s a a) => AsField s a | s -> a _Field :: AsField s a => Prism' s (Field a) _Unquoted :: AsField s a => Prism' s a _Quoted :: AsField s a => Prism' s (Quote, Unescaped a) -- | Build a quoted field with a normal string unescapedField :: Quote -> s -> Field s -- | The catamorphism for Field' foldField :: (s -> b) -> (Quote -> Unescaped s -> b) -> Field s -> b -- | Lens into the contents of a Field, regardless of whether it's quoted -- or unquoted fieldContents :: Lens (Field s) (Field t) s t instance GHC.Generics.Generic (Data.Sv.Syntax.Field.Field s) instance GHC.Show.Show s => GHC.Show.Show (Data.Sv.Syntax.Field.Field s) instance GHC.Classes.Ord s => GHC.Classes.Ord (Data.Sv.Syntax.Field.Field s) instance GHC.Classes.Eq s => GHC.Classes.Eq (Data.Sv.Syntax.Field.Field s) instance Data.Sv.Syntax.Field.AsField (Data.Sv.Syntax.Field.Field a) a instance Data.Sv.Syntax.Field.HasFields (Data.Sv.Syntax.Field.Field s) (Data.Sv.Syntax.Field.Field t) s t instance Control.DeepSeq.NFData s => Control.DeepSeq.NFData (Data.Sv.Syntax.Field.Field s) instance GHC.Base.Functor Data.Sv.Syntax.Field.Field instance Data.Foldable.Foldable Data.Sv.Syntax.Field.Field instance Data.Traversable.Traversable Data.Sv.Syntax.Field.Field -- | This module contains datatypes for Records. A record is a "line" or -- "row" of a CSV document module Data.Sv.Syntax.Record -- | A Record is a non-empty collection of Fields, implicitly -- separated by a separator (often a comma). newtype Record s Record :: NonEmptyVector (Spaced (Field s)) -> Record s [_fields] :: Record s -> NonEmptyVector (Spaced (Field s)) -- | Classy lenses for Record class HasRecord s t a b | s -> a, t -> b record :: HasRecord s t a b => Lens s t (Record a) (Record b) spacedFields :: HasRecord s t a b => Lens s t (NonEmptyVector (Spaced (Field a))) (NonEmptyVector (Spaced (Field b))) -- | A Record is isomorphic to a NonEmpty list of -- SpacedFields recordSpacedFieldsIso :: Iso (Record s) (Record a) (NonEmptyVector (Spaced (Field s))) (NonEmptyVector (Spaced (Field a))) -- | Build an empty record. -- -- According to RFC 4180, a record must have at least one field. But a -- field can be the empty string. So this is the closest we can get to an -- empty record. -- -- Note that this does not make Record a Monoid. It is not -- a lawful unit for the Semigroup operation. emptyRecord :: Monoid s => Record s -- | Build a Record with just one Field singleField :: Field s -> Record s -- | Build a Record given a NonEmpty list of its fields recordNel :: NonEmpty (SpacedField s) -> Record s -- | A collection of records, separated by newlines. data Records s EmptyRecords :: Records s Records :: (Record s) -> (Vector (Newline, Record s)) -> Records s -- | Classy lenses for Records class HasRecords c s | c -> s records :: HasRecords c s => Lens' c (Records s) traverseRecords :: HasRecords c s => Traversal' c (Record s) traverseNewlines :: HasRecords c s => Traversal' c Newline -- | Prism for an empty Records _EmptyRecords :: Prism' (Records s) () -- | Prism for a non-empty Records _NonEmptyRecords :: Prism (Records s) (Records t) (Record s, Vector (Newline, Record s)) (Record t, Vector (Newline, Record t)) -- | Convenience constructor for Records. -- -- This puts the same newline between all the records. mkRecords :: Newline -> NonEmpty (Record s) -> Records s -- | A record collection conaining one record singleRecord :: Record s -> Records s -- | Collect the list of Records from anything that -- HasRecords recordList :: HasRecords c s => c -> [Record s] instance GHC.Generics.Generic (Data.Sv.Syntax.Record.Records s) instance GHC.Show.Show s => GHC.Show.Show (Data.Sv.Syntax.Record.Records s) instance GHC.Classes.Ord s => GHC.Classes.Ord (Data.Sv.Syntax.Record.Records s) instance GHC.Classes.Eq s => GHC.Classes.Eq (Data.Sv.Syntax.Record.Records s) instance GHC.Generics.Generic (Data.Sv.Syntax.Record.Record s) instance Data.Semigroup.Semigroup (Data.Sv.Syntax.Record.Record s) instance GHC.Show.Show s => GHC.Show.Show (Data.Sv.Syntax.Record.Record s) instance GHC.Classes.Ord s => GHC.Classes.Ord (Data.Sv.Syntax.Record.Record s) instance GHC.Classes.Eq s => GHC.Classes.Eq (Data.Sv.Syntax.Record.Record s) instance Data.Sv.Syntax.Record.HasRecords (Data.Sv.Syntax.Record.Records s) s instance Control.DeepSeq.NFData s => Control.DeepSeq.NFData (Data.Sv.Syntax.Record.Records s) instance GHC.Base.Functor Data.Sv.Syntax.Record.Records instance Data.Foldable.Foldable Data.Sv.Syntax.Record.Records instance Data.Traversable.Traversable Data.Sv.Syntax.Record.Records instance Data.Sv.Syntax.Record.HasRecord (Data.Sv.Syntax.Record.Record a) (Data.Sv.Syntax.Record.Record b) a b instance Data.Sv.Syntax.Field.HasFields (Data.Sv.Syntax.Record.Record a) (Data.Sv.Syntax.Record.Record b) a b instance Control.DeepSeq.NFData s => Control.DeepSeq.NFData (Data.Sv.Syntax.Record.Record s) instance GHC.Base.Functor Data.Sv.Syntax.Record.Record instance Data.Foldable.Foldable Data.Sv.Syntax.Record.Record instance Data.Traversable.Traversable Data.Sv.Syntax.Record.Record -- | This file defines a datatype for a complete Sv document. The datatype -- preserves information such as whitespace so that the original text can -- be recovered. -- -- In the usual workflow, this type is only an intermediate stage between -- parsing and decoding. You can program against it directly using the -- provided functions and optics if you'd like. For an example of this -- see Requote.hs module Data.Sv.Syntax.Sv -- | Sv is a whitespace-preserving data type for separated values. -- Often the separator is a comma, but this type does not make that -- assumption so that it can be used for pipe- or tab-separated values as -- well. data Sv s Sv :: Separator -> Maybe (Header s) -> Records s -> [Newline] -> Sv s [_separatorSv] :: Sv s -> Separator [_maybeHeader] :: Sv s -> Maybe (Header s) [_records] :: Sv s -> Records s [_finalNewlines] :: Sv s -> [Newline] -- | Classy lenses for Sv class (HasRecords c s, HasSeparator c) => HasSv c s | c -> s sv :: HasSv c s => Lens' c (Sv s) maybeHeader :: HasSv c s => Lens' c (Maybe (Header s)) traverseHeader :: HasSv c s => Traversal' c (Header s) finalNewlines :: HasSv c s => Lens' c [Newline] -- | Classy lenses for Records class HasRecords c s | c -> s records :: HasRecords c s => Lens' c (Records s) traverseRecords :: HasRecords c s => Traversal' c (Record s) traverseNewlines :: HasRecords c s => Traversal' c Newline -- | Convenience constructor for Sv mkSv :: Separator -> Maybe (Header s) -> [Newline] -> Records s -> Sv s -- | An empty Sv emptySv :: Separator -> Sv s -- | Collect the list of Records from anything that -- HasRecords recordList :: HasRecords c s => c -> [Record s] -- | A Header is present in many CSV documents, usually listing the -- names of the columns. We keep this separate from the regular records. data Header s Header :: Record s -> Newline -> Header s -- | Classy lenses for Header class HasHeader s t a b | s -> a, t -> b, s b -> t, t a -> s header :: HasHeader s t a b => Lens s t (Header a) (Header b) headerNewline :: (HasHeader s t a b, (s ~ t)) => Lens s t Newline Newline headerRecord :: HasHeader s t a b => Lens s t (Record a) (Record b) headerNewline :: (HasHeader s t a b, (a ~ b)) => Lens s t Newline Newline -- | Used to build Svs that don't have a header noHeader :: Maybe (Header s) -- | Convenience constructor for Header, usually when you're -- building Svs mkHeader :: Record s -> Newline -> Maybe (Header s) -- | Does the Sv have a Header or not? A header is a row at -- the beginning of a file which contains the string names of each of the -- columns. -- -- If a header is present, it must not be decoded with the rest of the -- data. data Headedness Unheaded :: Headedness Headed :: Headedness -- | Classy lens for Headedness class HasHeadedness c headedness :: HasHeadedness c => Lens' c Headedness -- | Determine the Headedness of an Sv getHeadedness :: Sv s -> Headedness -- | By what are your values separated? The answer is often comma, -- but not always. -- -- A Separator is just a Char. It could be a sum type -- instead, since it will usually be comma or pipe, but our preference -- has been to be open here so that you can use whatever you'd like. -- There are test cases, for example, ensuring that you're free to use -- null-byte separated values if you so desire. type Separator = Char -- | Classy lens for Separator class HasSeparator c separator :: HasSeparator c => Lens' c Separator -- | The venerable comma separator. Used for CSV documents. comma :: Separator -- | The pipe separator. Used for PSV documents. pipe :: Separator -- | Tab is a separator too - why not? tab :: Separator instance GHC.Generics.Generic (Data.Sv.Syntax.Sv.Sv s) instance GHC.Show.Show s => GHC.Show.Show (Data.Sv.Syntax.Sv.Sv s) instance GHC.Classes.Ord s => GHC.Classes.Ord (Data.Sv.Syntax.Sv.Sv s) instance GHC.Classes.Eq s => GHC.Classes.Eq (Data.Sv.Syntax.Sv.Sv s) instance GHC.Show.Show Data.Sv.Syntax.Sv.Headedness instance GHC.Classes.Ord Data.Sv.Syntax.Sv.Headedness instance GHC.Classes.Eq Data.Sv.Syntax.Sv.Headedness instance GHC.Generics.Generic (Data.Sv.Syntax.Sv.Header s) instance Data.Traversable.Traversable Data.Sv.Syntax.Sv.Header instance Data.Foldable.Foldable Data.Sv.Syntax.Sv.Header instance GHC.Base.Functor Data.Sv.Syntax.Sv.Header instance GHC.Show.Show s => GHC.Show.Show (Data.Sv.Syntax.Sv.Header s) instance GHC.Classes.Ord s => GHC.Classes.Ord (Data.Sv.Syntax.Sv.Header s) instance GHC.Classes.Eq s => GHC.Classes.Eq (Data.Sv.Syntax.Sv.Header s) instance Data.Sv.Syntax.Sv.HasSv (Data.Sv.Syntax.Sv.Sv s) s instance Data.Sv.Syntax.Sv.HasSeparator GHC.Types.Char instance Data.Sv.Syntax.Sv.HasSeparator (Data.Sv.Syntax.Sv.Sv s) instance Control.DeepSeq.NFData s => Control.DeepSeq.NFData (Data.Sv.Syntax.Sv.Sv s) instance Data.Sv.Syntax.Record.HasRecords (Data.Sv.Syntax.Sv.Sv s) s instance GHC.Base.Functor Data.Sv.Syntax.Sv.Sv instance Data.Foldable.Foldable Data.Sv.Syntax.Sv.Sv instance Data.Traversable.Traversable Data.Sv.Syntax.Sv.Sv instance Data.Sv.Syntax.Sv.HasHeadedness Data.Sv.Syntax.Sv.Headedness instance Data.Sv.Syntax.Sv.HasHeader (Data.Sv.Syntax.Sv.Header a) (Data.Sv.Syntax.Sv.Header b) a b instance Data.Sv.Syntax.Record.HasRecord (Data.Sv.Syntax.Sv.Header a) (Data.Sv.Syntax.Sv.Header b) a b instance Data.Sv.Syntax.Field.HasFields (Data.Sv.Syntax.Sv.Header a) (Data.Sv.Syntax.Sv.Header b) a b instance Control.DeepSeq.NFData s => Control.DeepSeq.NFData (Data.Sv.Syntax.Sv.Header s) -- | Configuration to tell the parser what your file looks like. module Data.Sv.Parse.Options -- | An ParseOptions informs the parser how to parse your file. The -- type parameter will be some sort of string; often ByteString. -- -- A default is provided as defaultParseOptions, seen below. data ParseOptions s ParseOptions :: Separator -> Headedness -> Bool -> (String -> s) -> ParseOptions s -- | Which separator does the file use? Usually this is comma, but -- it can also be pipe, or any other Char -- (Separator = Char) [_parseSeparator] :: ParseOptions s -> Separator -- | Whether there is a header row with column names or not. [_headedness] :: ParseOptions s -> Headedness -- | If a blank line is encountered, should the parse finish, or treat it -- as an empty row and continue? [_endOnBlankLine] :: ParseOptions s -> Bool -- | How should I turn a String into this type? This is a detail used by -- the parser. [_encodeString] :: ParseOptions s -> String -> s -- | Classy lenses for ParseOptions class (HasSeparator s, HasHeadedness s) => HasParseOptions s t a b | s -> a, t -> b, s b -> t, t a -> s parseOptions :: HasParseOptions s t a b => Lens s t (ParseOptions a) (ParseOptions b) encodeString :: HasParseOptions s t a b => Lens s t (String -> a) (String -> b) endOnBlankLine :: (HasParseOptions s t a b, s ~ t) => Lens s t Bool Bool endOnBlankLine :: (HasParseOptions s t a b, s ~ t, a ~ b) => Lens s t Bool Bool -- | Classy lens for Separator class HasSeparator c separator :: HasSeparator c => Lens' c Separator -- | Classy lens for Headedness class HasHeadedness c headedness :: HasHeadedness c => Lens' c Headedness -- | defaultParseOptions is used to parse a CSV file featuring a -- header row, using Trifecta as the parsing library. It uses UTF-8 -- ByteStrings defaultParseOptions :: ParseOptions ByteString -- | The default is that a header is present. defaultHeadedness :: Headedness -- | The default separator. Alias for comma. defaultSeparator :: Separator instance Data.Sv.Parse.Options.HasParseOptions (Data.Sv.Parse.Options.ParseOptions a) (Data.Sv.Parse.Options.ParseOptions b) a b instance GHC.Base.Functor Data.Sv.Parse.Options.ParseOptions instance Data.Sv.Syntax.Sv.HasSeparator (Data.Sv.Parse.Options.ParseOptions s) instance Data.Sv.Syntax.Sv.HasHeadedness (Data.Sv.Parse.Options.ParseOptions s) module Data.Sv.Syntax module Data.Sv.Decode.Type -- | A 'Decode e s a' is for decoding some fields from a CSV row into our -- type a. -- -- The second type parameter (s) is the input string type -- (usually ByteString or Text). The first type -- parameter (e) is the type of strings which occur in errors. -- Under most circumstances you want these type paraters to coincide, but -- they don't have to. They are two separate type parameters instead of -- one so that Decode can have a Profunctor instance. -- -- There are primitive Decodes, and combinators for composing or -- otherwise manipulating them. In particular, Decode is an -- Applicative functor and an Alt from the semigroupoids -- package. -- -- Decode is not a Monad, but we can perform monad-like -- operations on it with >>== and bindDecode newtype Decode e s a Decode :: Compose (DecodeState s) (DecodeValidation e) a -> Decode e s a [unwrapDecode] :: Decode e s a -> Compose (DecodeState s) (DecodeValidation e) a -- | Decode' is Decode with the input and error types the -- same. You usually want them to be the same, and most primitives are -- set up this way. type Decode' s = Decode s s -- | Convenient constructor for Decode that handles all the newtype -- noise for you. buildDecode :: (Vector (SpacedField s) -> Ind -> (DecodeValidation e a, Ind)) -> Decode e s a -- | As we decode a row of data, we walk through its Fields. This -- Monad keeps track of our remaining Fields. newtype DecodeState s a DecodeState :: ReaderT (Vector (SpacedField s)) (State Ind) a -> DecodeState s a [getDecodeState] :: DecodeState s a -> ReaderT (Vector (SpacedField s)) (State Ind) a -- | Convenient function to run a DecodeState runDecodeState :: DecodeState s a -> Vector (SpacedField s) -> Ind -> (a, Ind) -- | Newtype for indices into the field vector newtype Ind Ind :: Int -> Ind -- | DecodeError is a value indicating what went wrong during a -- parse or decode. Its constructor indictates the type of error which -- occured, and there is usually an associated string with more -- finely-grained details. data DecodeError e -- | I was looking for another field, but I am at the end of the row UnexpectedEndOfRow :: DecodeError e -- | I should be at the end of the row, but I found extra fields ExpectedEndOfRow :: (Vector (SpacedField e)) -> DecodeError e -- | This decoder was built using the categorical primitive for -- categorical data UnknownCategoricalValue :: e -> [[e]] -> DecodeError e -- | The parser failed, meaning decoding proper didn't even begin BadParse :: e -> DecodeError e -- | Some other kind of decoding failure occured BadDecode :: e -> DecodeError e -- | DecodeErrors is a Semigroup full of DecodeError. -- It is used as the error side of a DecodeValidation. When -- multiple errors occur, they will be collected. newtype DecodeErrors e DecodeErrors :: (NonEmpty (DecodeError e)) -> DecodeErrors e -- | DecodeValidation is the error-accumulating Applicative -- underlying Decode type DecodeValidation e = Validation (DecodeErrors e) -- | An Validation is either a value of the type err or -- a, similar to Either. However, the Applicative -- instance for Validation accumulates errors using a -- Semigroup on err. In contrast, the -- Applicative for Either returns only the first error. -- -- A consequence of this is that Validation has no Bind -- or Monad instance. This is because such an instance would -- violate the law that a Monad's ap must equal the -- Applicative's <*> -- -- An example of typical usage can be found here. data Validation err a :: * -> * -> * Failure :: err -> Validation err a Success :: a -> Validation err a instance GHC.Base.Applicative (Data.Sv.Decode.Type.Decode e s) instance Data.Functor.Bind.Class.Apply (Data.Sv.Decode.Type.Decode e s) instance GHC.Base.Functor (Data.Sv.Decode.Type.Decode e s) instance GHC.Generics.Generic (Data.Sv.Decode.Type.DecodeErrors e) instance Data.Semigroup.Semigroup (Data.Sv.Decode.Type.DecodeErrors e) instance GHC.Show.Show e => GHC.Show.Show (Data.Sv.Decode.Type.DecodeErrors e) instance GHC.Classes.Ord e => GHC.Classes.Ord (Data.Sv.Decode.Type.DecodeErrors e) instance GHC.Classes.Eq e => GHC.Classes.Eq (Data.Sv.Decode.Type.DecodeErrors e) instance GHC.Generics.Generic (Data.Sv.Decode.Type.DecodeError e) instance GHC.Show.Show e => GHC.Show.Show (Data.Sv.Decode.Type.DecodeError e) instance GHC.Classes.Ord e => GHC.Classes.Ord (Data.Sv.Decode.Type.DecodeError e) instance GHC.Classes.Eq e => GHC.Classes.Eq (Data.Sv.Decode.Type.DecodeError e) instance Control.Monad.State.Class.MonadState Data.Sv.Decode.Type.Ind (Data.Sv.Decode.Type.DecodeState s) instance Control.Monad.Reader.Class.MonadReader (Data.Vector.Vector (Data.Sv.Syntax.Field.SpacedField s)) (Data.Sv.Decode.Type.DecodeState s) instance GHC.Base.Monad (Data.Sv.Decode.Type.DecodeState s) instance GHC.Base.Applicative (Data.Sv.Decode.Type.DecodeState s) instance Data.Functor.Bind.Class.Apply (Data.Sv.Decode.Type.DecodeState s) instance GHC.Base.Functor (Data.Sv.Decode.Type.DecodeState s) instance Data.Functor.Alt.Alt (Data.Sv.Decode.Type.Decode e s) instance Data.Profunctor.Unsafe.Profunctor (Data.Sv.Decode.Type.Decode e) instance GHC.Base.Functor Data.Sv.Decode.Type.DecodeErrors instance Control.DeepSeq.NFData e => Control.DeepSeq.NFData (Data.Sv.Decode.Type.DecodeErrors e) instance GHC.Base.Functor Data.Sv.Decode.Type.DecodeError instance Control.DeepSeq.NFData e => Control.DeepSeq.NFData (Data.Sv.Decode.Type.DecodeError e) instance Data.Functor.Bind.Class.Bind (Data.Sv.Decode.Type.DecodeState s) instance Data.Profunctor.Unsafe.Profunctor Data.Sv.Decode.Type.DecodeState module Data.Sv.Decode.Error -- | DecodeError is a value indicating what went wrong during a -- parse or decode. Its constructor indictates the type of error which -- occured, and there is usually an associated string with more -- finely-grained details. data DecodeError e -- | I was looking for another field, but I am at the end of the row UnexpectedEndOfRow :: DecodeError e -- | I should be at the end of the row, but I found extra fields ExpectedEndOfRow :: (Vector (SpacedField e)) -> DecodeError e -- | This decoder was built using the categorical primitive for -- categorical data UnknownCategoricalValue :: e -> [[e]] -> DecodeError e -- | The parser failed, meaning decoding proper didn't even begin BadParse :: e -> DecodeError e -- | Some other kind of decoding failure occured BadDecode :: e -> DecodeError e -- | DecodeErrors is a Semigroup full of DecodeError. -- It is used as the error side of a DecodeValidation. When -- multiple errors occur, they will be collected. newtype DecodeErrors e DecodeErrors :: (NonEmpty (DecodeError e)) -> DecodeErrors e -- | Build a failing DecodeValidation decodeError :: DecodeError e -> DecodeValidation e a -- | Fail with UnexpectedEndOfRow unexpectedEndOfRow :: DecodeValidation e a -- | Fail with ExpectedEndOfRow. This takes the rest of the row, so -- that it can be displayed to the user. expectedEndOfRow :: Vector (SpacedField e) -> DecodeValidation e a -- | Fail with UnknownCategoricalValue. It takes the unknown value -- and the list of good categorical values. -- -- This mostly exists to be used by the categorical function. unknownCategoricalValue :: e -> [[e]] -> DecodeValidation e a -- | Fail with BadParse with the given message. This is for when the -- parse step fails, and decoding does not even begin. badParse :: e -> DecodeValidation e a -- | Fail with BadDecode with the given message. This is something -- of a generic error for when decoding a field goes wrong. badDecode :: e -> DecodeValidation e a -- | Build a DecodeValidation from an Either validateEither :: Either (DecodeError e) a -> DecodeValidation e a -- | Build a DecodeValidation from an Either, given a -- function to build the error. validateEither' :: (e -> DecodeError e') -> Either e a -> DecodeValidation e' a -- | Build a DecodeValidation from a Maybe. You have to -- supply an error to use in the Nothing case validateMaybe :: DecodeError e -> Maybe b -> DecodeValidation e b -- | Build a DecodeValidation from a function that returns a -- Maybe You have to supply an error to use in the Nothing -- case validateMaybe' :: (a -> Maybe b) -> DecodeError e -> a -> DecodeValidation e b -- | Helper to convert Text.Trifecta Result to Either. trifectaResultToEither :: Result a -> Either String a -- | Convert a Text.Trifecta Result to a -- DecodeValidation validateTrifectaResult :: (String -> DecodeError e) -> Result a -> DecodeValidation e a -- | bindValidation binds through an Validation, which is useful -- for composing Validations sequentially. Note that despite having a -- bind function of the correct type, Validation is not a monad. The -- reason is, this bind does not accumulate errors, so it does not agree -- with the Applicative instance. -- -- There is nothing wrong with using this function, it just does not make -- a valid Monad instance. bindValidation :: () => Validation e a -> (a -> Validation e b) -> Validation e b -- | This module is considered an implementation detail. As the -- Internal module name suggests, this module is exempt from the -- PVP, so depend on it at your own risk! These functions exist to be -- called by Data.Sv.Print. module Data.Sv.Print.Internal -- | Convert a Newline to a ByteString Builder printNewline :: Newline -> Builder -- | Convert a Field to a ByteString Builder printField :: PrintOptions s -> Field s -> Builder -- | Convert a SpacedField to a ByteString Builder printSpaced :: PrintOptions s -> SpacedField s -> Builder -- | Convert a Record to a ByteString Builder printRecord :: PrintOptions s -> Separator -> Record s -> Builder -- | Convert Records to a ByteString Builder. printRecords :: PrintOptions s -> Separator -> Records s -> Builder -- | Convert Header to a ByteString Builder. printHeader :: PrintOptions s -> Separator -> Header s -> Builder -- | Printing is the process of turning an Sv into a textual -- representation, such as a ByteString. -- -- If you want to turn your data type into a textual representation, you -- should look instead at Data.Sv.Encode module Data.Sv.Print -- | Print an Sv to a ByteString value. printSv :: Sv ByteString -> ByteString -- | Print an Sv to a lazy ByteString value. printSvLazy :: Sv ByteString -> ByteString -- | Converts the given Sv into a strict ByteString printSv' :: PrintOptions s -> Sv s -> ByteString -- | Converts the given Sv into a lazy ByteString printSvLazy' :: PrintOptions s -> Sv s -> ByteString -- | Print an Sv containing Text to a ByteString printSvText :: Sv Text -> ByteString -- | Print an Sv containing Text to a ByteString printSvTextLazy :: Sv Text -> ByteString -- | Writes an sv to a file. This goes directly from a Builder, so -- it is more efficient than calling printSv or printSvLazy -- and writing the result to a file. writeSvToFile :: FilePath -> Sv ByteString -> IO () -- | Writes an sv to a file handle. This goes directly from a -- Builder, so it is more efficient than calling printSv or -- printSvLazy and writing the result to the handle. writeSvToHandle :: Handle -> Sv ByteString -> IO () -- | Writes an sv to a file. This goes directly from a Builder, so -- it is more efficient than calling printSv or printSvLazy -- and writing the result to a file. -- -- This version is polymorphic, but as a penalty you have to tell me how -- to get a Bytestring Builder. writeSvToFile' :: PrintOptions s -> FilePath -> Sv s -> IO () -- | Writes an sv to a file handle. This goes directly from a -- Builder, so it is more efficient than calling printSv or -- printSvLazy and writing the result to the handle. -- -- This version is polymorphic, but as a penalty you have to tell me how -- to get a Bytestring Builder. writeSvToHandle' :: PrintOptions s -> Handle -> Sv s -> IO () -- | This module contains internal implementation details of sv's parser. -- As the Internal name suggests, this file is exempt from the PVP. -- Depend on this module at your own risk! module Data.Sv.Parse.Internal -- | Parse an Sv separatedValues :: CharParsing m => ParseOptions s -> m (Sv s) -- | Maybe parse the header row of a CSV file, depending on the given -- Headedness header :: CharParsing m => ParseOptions s -> m (Maybe (Header s)) -- | Parse a field, be it quoted or unquoted field :: CharParsing m => Separator -> (String -> s) -> m (Field s) -- | Parse a field surrounded by single quotes singleQuotedField :: CharParsing m => (String -> s) -> m (Field s) -- | Parse a field surrounded by double quotes doubleQuotedField :: CharParsing m => (String -> s) -> m (Field s) -- | Parse a field that is not surrounded by quotes unquotedField :: CharParsing m => Separator -> (String -> s) -> m (Field s) -- | Combinator to parse some data surrounded by spaces spaced :: CharParsing m => Separator -> m a -> m (Spaced a) -- | Parse a field with its surrounding spacing spacedField :: CharParsing m => Separator -> (String -> s) -> m (Spaced (Field s)) -- | Parse an entire record, or "row" record :: CharParsing m => ParseOptions s -> m (Record s) -- | Parse many records, or "rows" records :: CharParsing m => ParseOptions s -> m (Records s) -- | Parse zero or many newlines ending :: CharParsing m => ParseOptions s -> m [Newline] module Data.Sv.Parse -- | Parse a ByteString as an Sv. -- -- This version uses Trifecta, hence it assumes its input is UTF-8 -- encoded. parseSv :: ParseOptions ByteString -> ByteString -> Either ByteString (Sv ByteString) -- | Parse some text as an Sv. -- -- This version lets you choose which parsing library to use by providing -- an SvParser. Common selections are trifecta and -- attoparsecByteString. parseSv' :: SvParser s -> ParseOptions s -> s -> Either s (Sv s) -- | Load a file and parse it as an Sv. -- -- This version uses Trifecta, hence it assumes its input is UTF-8 -- encoded. parseSvFromFile :: MonadIO m => ParseOptions ByteString -> FilePath -> m (Either ByteString (Sv ByteString)) -- | Load a file and parse it as an Sv. -- -- This version lets you choose which parsing library to use by providing -- an SvParser. Common selections are trifecta and -- attoparsecByteString. parseSvFromFile' :: MonadIO m => SvParser s -> ParseOptions s -> FilePath -> m (Either s (Sv s)) -- | Parse an Sv separatedValues :: CharParsing m => ParseOptions s -> m (Sv s) -- | Which parsing library should be used to parse the document? -- -- The parser is written in terms of the parsers library, -- meaning it can be instantiated to several different parsing libraries. -- By default, we use trifecta, because Text.Trifectas -- error messages are so helpful. attoparsecByteString is faster -- though, if your input is ASCII and you care a lot about speed. -- -- It is worth noting that Trifecta assumes UTF-8 encoding of the input -- data. UTF-8 is backwards-compatible with 7-bit ASCII, so this will -- work for many documents. However, not all documents are ASCII or -- UTF-8. For example, our species.csv test file is Windows-1252, -- which is a non-ISO extension of latin1 8-bit ASCII. For documents -- encoded as Windows-1252, Trifecta's assumption is invalid and parse -- errors result. Attoparsec works fine for this character -- encoding, but it wouldn't work well on a UTF-8 encoded document -- including non-ASCII characters. data SvParser s SvParser :: (ParseOptions s -> s -> Either String (Sv s)) -> (ParseOptions s -> FilePath -> IO (Either String (Sv s))) -> SvParser s [runSvParser] :: SvParser s -> ParseOptions s -> s -> Either String (Sv s) [runSvParserFromFile] :: SvParser s -> ParseOptions s -> FilePath -> IO (Either String (Sv s)) -- | An SvParser that uses Text.Trifecta. Trifecta assumes -- its input is UTF-8, and provides helpful clang-style error messages. trifecta :: SvParser ByteString -- | An SvParser that uses Data.Attoparsec.ByteString. This -- is the fastest provided SvParser, but it has poorer error -- messages. attoparsecByteString :: SvParser ByteString -- | An SvParser that uses Data.Attoparsec.Text. This is -- helpful if your input is in the form of Text. attoparsecText :: SvParser Text -- | Options to configure encoding module Data.Sv.Encode.Options -- | These are options to configure encoding. A default is provided as -- defaultEncodeOptions. data EncodeOptions EncodeOptions :: Separator -> Spaces -> Spaces -> Maybe Quote -> Newline -> Bool -> EncodeOptions -- | Are your values separated by commas, tabs, or something else? Default: -- comma [_encodeSeparator] :: EncodeOptions -> Separator -- | Between a comma and the next value, would you like some spacing? -- Default: no spacing [_spacingBefore] :: EncodeOptions -> Spaces -- | Between a value and the next comma, would you like some spacing? -- Default: no spacing [_spacingAfter] :: EncodeOptions -> Spaces -- | Would you like quotes around your values? If so, double quotes or -- single? Deafult: Double quotes [_quote] :: EncodeOptions -> Maybe Quote -- | What kind of newline would you like? Default: CRLF [_newline] :: EncodeOptions -> Newline -- | Should the file be terminated with a newline? Default: No [_terminalNewline] :: EncodeOptions -> Bool -- | Classy lenses for EncodeOptions -- --
--   import Control.Lens
--   
--   defaultEncodeOptinons & quote .~ Just DoubleQuote & newline .~ LF
--   
class HasSeparator c => HasEncodeOptions c encodeOptions :: HasEncodeOptions c => Lens' c EncodeOptions newline :: HasEncodeOptions c => Lens' c Newline quote :: HasEncodeOptions c => Lens' c (Maybe Quote) spacingAfter :: HasEncodeOptions c => Lens' c Spaces spacingBefore :: HasEncodeOptions c => Lens' c Spaces terminalNewline :: HasEncodeOptions c => Lens' c Bool -- | Classy lens for Separator class HasSeparator c separator :: HasSeparator c => Lens' c Separator -- | The default options for encoding. -- -- The default is a CSV file with double-quotes, CRLF lines, no spacing -- around fields, and no terminating newline. defaultEncodeOptions :: EncodeOptions instance Data.Sv.Encode.Options.HasEncodeOptions Data.Sv.Encode.Options.EncodeOptions instance Data.Sv.Syntax.Sv.HasSeparator Data.Sv.Encode.Options.EncodeOptions -- | The core type for encoding module Data.Sv.Encode.Type -- | An Encode converts its argument into one or more textual -- fields, to be written out as CSV. -- -- It is Semigroup, Contravariant', Divisible, and -- Decidable, allowing for composition of these values to build -- bigger Encodes from smaller ones. newtype Encode a Encode :: (EncodeOptions -> a -> Seq Builder) -> Encode a [getEncode] :: Encode a -> EncodeOptions -> a -> Seq Builder instance GHC.Base.Monoid (Data.Sv.Encode.Type.Encode a) instance Data.Semigroup.Semigroup (Data.Sv.Encode.Type.Encode a) instance Data.Functor.Contravariant.Contravariant Data.Sv.Encode.Type.Encode instance Data.Functor.Contravariant.Divisible.Divisible Data.Sv.Encode.Type.Encode instance Data.Functor.Contravariant.Divisible.Decidable Data.Sv.Encode.Type.Encode -- | This module is intended to be imported qualified as follows -- --
--   import Data.Sv.Encode as E
--   
-- -- To produce a CSV file from data types, build an Encode for your -- data type. This module contains primitives, combinators, and type -- class instances to help you to do so. -- -- Encode is a Contravariant functor, as well as a -- Divisible and Decidable. Divisible is the -- contravariant form of Applicative, while Decidable is -- the contravariant form of Alternative. These type classes will -- provide useful combinators for working with Encodes. -- -- Specialised to Encode, the function divide from -- Divisible has the type: -- --
--   divide :: (a -> (b,c)) -> Encode b -> Encode c -> Encode a
--   
-- -- which can be read "if a can be split into b and -- c, and I can handle b, and I can handle c, -- then I can handle a". -- -- Here the "I can handle" part corresponds to the Encode. If we -- think of (covariant) functors as being "full of" a, then we -- can think of contravariant functors as being "able to handle" -- a. -- -- How does it work? Perform the split on the a, handle the -- b by converting it into some text, handle the c by -- also converting it to some text, then put each of those text fragments -- into their own field in the CSV. -- -- Similarly, the function choose from Decidable, -- specialsed to Encode, has the type: -- --
--   choose :: (a -> Either b c) -> Encode b -> Encode c -> Encode a
--   
-- -- which can be read "if a is either b or c, -- and I can handle b, and I can handle c, then I can -- handle a". -- -- This works by performing the split, then checking whether b -- or c resulted, then using the appropriate Encode. -- -- For an example of encoding, see Encoding.hs module Data.Sv.Encode -- | An Encode converts its argument into one or more textual -- fields, to be written out as CSV. -- -- It is Semigroup, Contravariant', Divisible, and -- Decidable, allowing for composition of these values to build -- bigger Encodes from smaller ones. newtype Encode a Encode :: (EncodeOptions -> a -> Seq Builder) -> Encode a [getEncode] :: Encode a -> EncodeOptions -> a -> Seq Builder -- | Make an Encode from a function that builds one Field. mkEncodeBS :: (a -> ByteString) -> Encode a -- | Make an Encode from a function that builds one Field. mkEncodeWithOpts :: (EncodeOptions -> a -> Builder) -> Encode a -- | Make an encode from any function that returns a ByteString -- Builder. unsafeBuilder :: (a -> Builder) -> Encode a -- | Encode the given list with the given Encode, configured by the -- given EncodeOptions. encode :: Encode a -> EncodeOptions -> [a] -> ByteString -- | Encode, writing the output to a file handle. encodeToHandle :: Encode a -> EncodeOptions -> [a] -> Handle -> IO () -- | Encode, writing to a file. This is way is more efficient than encoding -- to a ByteString and then writing to file. encodeToFile :: Encode a -> EncodeOptions -> [a] -> FilePath -> IO () -- | Encode to a ByteString Builder, which is useful if you are -- going to combine the output with other ByteStrings. encodeBuilder :: Encode a -> EncodeOptions -> [a] -> Builder -- | Encode one row only encodeRow :: Encode a -> EncodeOptions -> a -> ByteString -- | Encode one row only, as a ByteString Builder encodeRowBuilder :: Encode a -> EncodeOptions -> a -> Builder -- | Build an Sv rather than going straight to ByteString. -- This allows you to query the Sv or run sanity checks. encodeSv :: Encode a -> EncodeOptions -> Maybe (NonEmpty ByteString) -> [a] -> Sv ByteString -- | Encode this ByteString every time, ignoring the input. const :: ByteString -> Encode a -- | Build an Encode using a type's Show instance. show :: Show a => Encode a -- | Don't encode anything. nop :: Encode a -- | Encode anything as the empty string. empty :: Encode a -- | Lift an Encode to be able to hanlde Maybe, by using the empty -- string in the case of Nothing orEmpty :: Encode a -> Encode (Maybe a) -- | Encode a single Char char :: Encode Char -- | Encode an Int int :: Encode Int -- | Encode an Integer integer :: Encode Integer -- | Encode a Float float :: Encode Float -- | Encode a Double double :: Encode Double -- | Encode a Bool as False or True boolTrueFalse :: Encode Bool -- | Encode a Bool as false or true booltruefalse :: Encode Bool -- | Encode a Bool as no or yes boolyesno :: Encode Bool -- | Encode a Bool as No or Yes boolYesNo :: Encode Bool -- | Encode a Bool as N or Y boolYN :: Encode Bool -- | Encode a Bool as 0 or 1 bool10 :: Encode Bool -- | Encode a String string :: Encode String -- | Encode a Text text :: Encode Text -- | Encode a strict ByteString byteString :: Encode ByteString -- | Encode a lazy ByteString lazyByteString :: Encode ByteString -- | Encode a list as a whole row at once, using the same Encode for -- every element row :: Encode s -> Encode [s] -- | Build an Encode for Maybe given a Just and a -- Nothing encode. (?>) :: Encode a -> Encode () -> Encode (Maybe a) -- | Build an Encode for Maybe given a Nothing and a -- Just encode. ( Encode a -> Encode (Maybe a) -- | Build an Encode for Maybe given a Just encode and -- a ByteString for the Nothing case. (?>>) :: Encode a -> ByteString -> Encode (Maybe a) -- | Build an Encode for Maybe given a ByteString for -- the Nothing case and a Just encode. (< Encode a -> Encode (Maybe a) -- | Given an optic from s to a, Try to use it to build -- an encode. -- --
--   encodeOf :: Iso'       s a -> Encode a -> Encode s
--   encodeOf :: Lens'      s a -> Encode a -> Encode s
--   encodeOf :: Prism'     s a -> Encode a -> Encode s
--   encodeOf :: Traversal' s a -> Encode a -> Encode s
--   encodeOf :: Fold       s a -> Encode a -> Encode s
--   encodeOf :: Getter     s a -> Encode a -> Encode s
--   
-- -- This is very useful when you have a prism for each constructor of your -- type. You can define an Encode as follows: -- --
--   myEitherEncode :: Encode a -> Encode b -> Encode (Either a b)
--   myEitherEncode encA encB = encodeOf _Left encA <> encodeOf _Right encB
--   
-- -- In this example, when the prism lookup returns Nothing, the -- empty encoder is returned. This is the mempty for the -- Encode monoid, so it won't add a field to the resulting CSV. -- This is the behaviour you want for combining a collection of prisms. -- -- But this encoder also works with lenses (or weaker optics), which will -- never fail their lookup, in which case it never returns mempty. -- So this actually does the right thing for both sum and product types. encodeOf :: Getting (First a) s a -> Encode a -> Encode s -- | Like encodeOf, but you can handle Nothing however you'd -- like. In encodeOf, it is handled by the Encode which does -- nothing, but for example you might like to use orEmpty to -- encode an empty field. encodeOfMay :: Getting (First a) s a -> Encode (Maybe a) -> Encode s -- | Encode a String really quickly. If the string has quotes in it, -- they will not be escaped properly, so the result maybe not be valid -- CSV unsafeString :: Encode String -- | Encode Text really quickly. If the text has quotes in it, they -- will not be escaped properly, so the result maybe not be valid CSV unsafeText :: Encode Text -- | Encode a ByteString really quickly. If the string has quotes in -- it, they will not be escaped properly, so the result maybe not be -- valid CSV unsafeByteString :: Encode ByteString -- | Encode a ByteString really quickly. If the string has quotes in -- it, they will not be escaped properly, so the result maybe not be -- valid CSV unsafeLazyByteString :: Encode ByteString -- | Encode ByteString Builder really quickly. If the builder builds -- a string with quotes in it, they will not be escaped properly, so the -- result maybe not be valid CSV unsafeByteStringBuilder :: Encode Builder -- | Encode this ByteString really quickly every time, ignoring the -- input. If the string has quotes in it, they will not be escaped -- properly, so the result maybe not be valid CSV unsafeConst :: ByteString -> Encode a -- | This module contains data structures, combinators, and primitives for -- decoding an Sv into a list of your Haskell datatype. -- -- A file can be read with parseDecodeFromFile. If you already -- have the text data in memory, it can be decoded with -- parseDecode. You will need a Decode for your desired -- type. -- -- A Decode can be built using the primitives in this file. -- Decode is an Applicative and an Alt, allowing for -- composition of these values with <*> and <!> -- -- The primitive Decodes in this file which use ByteString -- expect UTF-8 encoding. The Decode type has an instance of -- Profunctor, so you can lmap or alterInput to -- reencode on the way in. -- -- This module is intended to be imported qualified like so -- --
--   import qualified Data.Sv.Decode as D
--   
module Data.Sv.Decode -- | A 'Decode e s a' is for decoding some fields from a CSV row into our -- type a. -- -- The second type parameter (s) is the input string type -- (usually ByteString or Text). The first type -- parameter (e) is the type of strings which occur in errors. -- Under most circumstances you want these type paraters to coincide, but -- they don't have to. They are two separate type parameters instead of -- one so that Decode can have a Profunctor instance. -- -- There are primitive Decodes, and combinators for composing or -- otherwise manipulating them. In particular, Decode is an -- Applicative functor and an Alt from the semigroupoids -- package. -- -- Decode is not a Monad, but we can perform monad-like -- operations on it with >>== and bindDecode newtype Decode e s a Decode :: Compose (DecodeState s) (DecodeValidation e) a -> Decode e s a [unwrapDecode] :: Decode e s a -> Compose (DecodeState s) (DecodeValidation e) a -- | Decode' is Decode with the input and error types the -- same. You usually want them to be the same, and most primitives are -- set up this way. type Decode' s = Decode s s -- | An Validation is either a value of the type err or -- a, similar to Either. However, the Applicative -- instance for Validation accumulates errors using a -- Semigroup on err. In contrast, the -- Applicative for Either returns only the first error. -- -- A consequence of this is that Validation has no Bind -- or Monad instance. This is because such an instance would -- violate the law that a Monad's ap must equal the -- Applicative's <*> -- -- An example of typical usage can be found here. data Validation err a :: * -> * -> * Failure :: err -> Validation err a Success :: a -> Validation err a -- | DecodeValidation is the error-accumulating Applicative -- underlying Decode type DecodeValidation e = Validation (DecodeErrors e) -- | DecodeError is a value indicating what went wrong during a -- parse or decode. Its constructor indictates the type of error which -- occured, and there is usually an associated string with more -- finely-grained details. data DecodeError e -- | I was looking for another field, but I am at the end of the row UnexpectedEndOfRow :: DecodeError e -- | I should be at the end of the row, but I found extra fields ExpectedEndOfRow :: (Vector (SpacedField e)) -> DecodeError e -- | This decoder was built using the categorical primitive for -- categorical data UnknownCategoricalValue :: e -> [[e]] -> DecodeError e -- | The parser failed, meaning decoding proper didn't even begin BadParse :: e -> DecodeError e -- | Some other kind of decoding failure occured BadDecode :: e -> DecodeError e -- | DecodeErrors is a Semigroup full of DecodeError. -- It is used as the error side of a DecodeValidation. When -- multiple errors occur, they will be collected. newtype DecodeErrors e DecodeErrors :: (NonEmpty (DecodeError e)) -> DecodeErrors e -- | Decodes a sv into a list of its values using the provided -- Decode decode :: Decode' s a -> Sv s -> DecodeValidation s [a] -- | Parse a ByteString as an Sv, and then decode it with the given -- decoder. -- -- This version uses Trifecta to parse the ByteString, -- which is assumed to be UTF-8 encoded. If you want a different library, -- use parseDecode'. parseDecode :: Decode' ByteString a -> ParseOptions ByteString -> ByteString -> DecodeValidation ByteString [a] -- | Parse text as an Sv, and then decode it with the given decoder. -- -- This version lets you choose which parsing library to use by providing -- an SvParser. Common selections are trifecta and -- attoparsecByteString. parseDecode' :: SvParser s -> Decode' s a -> ParseOptions s -> s -> DecodeValidation s [a] -- | Load a file, parse it, and decode it. -- -- This version uses Trifecta to parse the file, which is assumed to be -- UTF-8 encoded. parseDecodeFromFile :: MonadIO m => Decode' ByteString a -> ParseOptions ByteString -> FilePath -> m (DecodeValidation ByteString [a]) -- | Load a file, parse it, and decode it. -- -- This version lets you choose which parsing library to use by providing -- an SvParser. Common selections are trifecta and -- attoparsecByteString. parseDecodeFromFile' :: MonadIO m => SvParser s -> Decode' s a -> ParseOptions s -> FilePath -> m (DecodeValidation s [a]) -- | Build a Decode, given a function that returns Maybe. -- -- Return the given error if the function returns Nothing. decodeMay :: DecodeError e -> (s -> Maybe a) -> Decode e s a -- | Build a Decode, given a function that returns Either. decodeEither :: (s -> Either (DecodeError e) a) -> Decode e s a -- | Build a Decode, given a function that returns Either, -- and a function to build the error. decodeEither' :: (e -> DecodeError e') -> (s -> Either e a) -> Decode e' s a -- | Map over the errors of a Decode -- -- To map over the other two parameters, use the Profunctor -- instance. mapErrors :: (e -> x) -> Decode e s a -> Decode x s a -- | This transforms a Decode' s a into a Decode' t a. It -- needs functions in both directions because the errors can include -- fragments of the input. -- --
--   alterInput :: (s -> t) -> (t -> s) -> Decode' s a -> Decode' t a
--   
alterInput :: (e -> x) -> (t -> s) -> Decode e s a -> Decode x t a -- | Get the contents of a field without doing any decoding. This never -- fails. contents :: Decode e s s -- | Returns the field contents. This keeps the spacing around an unquoted -- field. untrimmed :: Monoid s => (HorizontalSpace -> s) -> Decode e s s -- | Succeeds with the whole field structure, including spacing and quoting -- information raw :: Decode e s (SpacedField s) -- | Get a field that's a single char. This will fail if there are mulitple -- characters in the field. char :: Decode' ByteString Char -- | Get the contents of a field as a bytestring. -- -- Alias for contents byteString :: Decode' ByteString ByteString -- | Get the contents of a UTF-8 encoded field as Text -- -- This will also work for ASCII text, as ASCII is a subset of UTF-8 utf8 :: Decode' ByteString Text -- | Get the contents of a field as a lazy Text lazyUtf8 :: Decode' ByteString Text -- | Get the contents of a field as a lazy ByteString lazyByteString :: Decode' ByteString ByteString -- | Get the contents of a field as a String string :: Decode' ByteString String -- | Decode a UTF-8 ByteString field as an Int int :: Decode' ByteString Int -- | Decode a UTF-8 ByteString field as an Integer integer :: Decode' ByteString Integer -- | Decode a UTF-8 ByteString field as a Float float :: Decode' ByteString Float -- | Decode a UTF-8 ByteString field as a Double double :: Decode' ByteString Double -- | Decode a field as a Bool -- -- This aims to be tolerant to different forms a boolean might take. boolean :: (IsString s, Ord s) => Decode' s Bool -- | Decode a field as a Bool. This version lets you provide the -- fromString function that's right for you, since IsString on a -- ByteString will do the wrong thing in the case of many -- encodings such as UTF-16 or UTF-32. -- -- This aims to be tolerant to different forms a boolean might take. boolean' :: Ord s => (String -> s) -> Decode' s Bool -- | Throw away the contents of a field. This is useful for skipping -- unneeded fields. ignore :: Decode e s () -- | Throw away the contents of a field, and return the given value. replace :: a -> Decode e s a -- | Decode exactly the given string, or else fail. exactly :: (Semigroup s, Eq s, IsString s) => s -> Decode' s s -- | Succeed only when the given field is the empty string. -- -- The empty string surrounded in quotes or spaces is still the empty -- string. emptyField :: (Eq s, IsString s, Semigroup s) => Decode' s () -- | Grab the whole row as a Vector row :: Decode e s (Vector s) -- | Grab the whole row, including all spacing and quoting information, as -- a Vector rowWithSpacing :: Decode e s (Vector (SpacedField s)) -- | Choose the leftmost Decode that succeeds. Alias for -- <!> choice :: Decode e s a -> Decode e s a -> Decode e s a -- | Choose the leftmost Decode that succeeds. Alias for -- asum1 element :: NonEmpty (Decode e s a) -> Decode e s a -- | Try the given Decode. If it fails, succeed without consuming -- anything. -- -- This usually isn't what you want. ignoreFailure and -- orEmpty are more likely what you are after. optionalField :: Decode e s a -> Decode e s (Maybe a) -- | Try the given Decode. If it fails, instead succeed with -- Nothing. ignoreFailure :: Decode e s a -> Decode e s (Maybe a) -- | If the field is the empty string, succeed with Nothing. -- Otherwise try the given Decode. orEmpty :: (Eq s, IsString s, Semigroup s) => Decode' s a -> Decode' s (Maybe a) -- | Try the first, then try the second, and wrap the winner in an -- Either. -- -- This is left-biased, meaning if they both succeed, left wins. either :: Decode e s a -> Decode e s b -> Decode e s (Either a b) -- | Try the given decoder, otherwise succeed with the given value. orElse :: Decode e s a -> a -> Decode e s a -- | Try the given decoder, or if it fails succeed with the given value, in -- an Either. orElseE :: Decode e s b -> a -> Decode e s (Either a b) -- | Decode categorical data, given a list of the values and the strings -- which match them. -- -- Usually this is used with sum types with nullary constructors. -- --
--   data TrafficLight = Red | Amber | Green
--   categorical [(Red, "red"), (Amber, "amber"), (Green, "green")]
--   
categorical :: (Ord s, Show a) => [(a, s)] -> Decode' s a -- | Decode categorical data, given a list of the values and lists of -- strings which match them. -- -- This version allows for multiple strings to match each value, which is -- useful for when the categories are inconsistently labelled. -- --
--   data TrafficLight = Red | Amber | Green
--   categorical' [(Red, ["red", "R"]), (Amber, ["amber", "orange", "A"]), (Green, ["green", "G"])]
--   
-- -- For another example of its usage, see the source for boolean. categorical' :: forall s a. (Ord s, Show a) => [(a, [s])] -> Decode' s a -- | This can be used to build a Decode whose value depends on the -- result of another Decode. This is especially useful since -- Decode is not a Monad. -- -- If you need something like this but with more power, look at -- bindDecode (>>==) :: Decode e s a -> (a -> DecodeValidation e b) -> Decode e s b infixl 1 >>== -- | flipped >>== (==<<) :: (a -> DecodeValidation e b) -> Decode e s a -> Decode e s b infixr 1 ==<< -- | Bind through a Decode. -- -- This bind does not agree with the Applicative instance because -- it does not accumulate multiple error values. This is a violation of -- the Monad laws, meaning Decode is not a Monad. -- -- That is not to say that there is anything wrong with using this -- function. It can be quite useful. bindDecode :: Decode e s a -> (a -> Decode e s b) -> Decode e s b -- | Use the Readable instance to try to decode the given value. decodeRead :: Readable a => Decode' ByteString a -- | Use the Readable instance to try to decode the given value, or -- fail with the given error message. decodeRead' :: Readable a => ByteString -> Decode' ByteString a -- | Use the Readable instance to try to decode the given value, or -- use the value to build an error message. decodeReadWithMsg :: Readable a => (ByteString -> e) -> Decode e ByteString a -- | Build a Decode from a Trifecta parser withTrifecta :: Parser a -> Decode' ByteString a -- | Build a Decode from an Attoparsec parser withAttoparsec :: Parser a -> Decode' ByteString a -- | Build a Decode from a Parsec parser withParsec :: Parsec ByteString () a -> Decode' ByteString a -- | Run a Decode, and based on its errors build a new -- Decode. onError :: Decode e s a -> (DecodeErrors e -> Decode e s a) -> Decode e s a -- | Build a failing DecodeValidation decodeError :: DecodeError e -> DecodeValidation e a -- | Fail with UnexpectedEndOfRow unexpectedEndOfRow :: DecodeValidation e a -- | Fail with ExpectedEndOfRow. This takes the rest of the row, so -- that it can be displayed to the user. expectedEndOfRow :: Vector (SpacedField e) -> DecodeValidation e a -- | Fail with UnknownCategoricalValue. It takes the unknown value -- and the list of good categorical values. -- -- This mostly exists to be used by the categorical function. unknownCategoricalValue :: e -> [[e]] -> DecodeValidation e a -- | Fail with BadParse with the given message. This is for when the -- parse step fails, and decoding does not even begin. badParse :: e -> DecodeValidation e a -- | Fail with BadDecode with the given message. This is something -- of a generic error for when decoding a field goes wrong. badDecode :: e -> DecodeValidation e a -- | Build a DecodeValidation from an Either validateEither :: Either (DecodeError e) a -> DecodeValidation e a -- | Build a DecodeValidation from an Either, given a -- function to build the error. validateEither' :: (e -> DecodeError e') -> Either e a -> DecodeValidation e' a -- | Build a DecodeValidation from a Maybe. You have to -- supply an error to use in the Nothing case validateMaybe :: DecodeError e -> Maybe b -> DecodeValidation e b -- | Build a DecodeValidation from a function that returns a -- Maybe You have to supply an error to use in the Nothing -- case validateMaybe' :: (a -> Maybe b) -> DecodeError e -> a -> DecodeValidation e b -- | Convenience to get the underlying function out of a Decode in a useful -- form runDecode :: Decode e s a -> Vector (SpacedField s) -> Ind -> (DecodeValidation e a, Ind) -- | Convenient constructor for Decode that handles all the newtype -- noise for you. buildDecode :: (Vector (SpacedField s) -> Ind -> (DecodeValidation e a, Ind)) -> Decode e s a -- | Build a Decode from a function. -- -- This version gives you just the contents of the field, with no -- information about the spacing or quoting around that field. mkDecode :: (s -> DecodeValidation e a) -> Decode e s a -- | Build a Decode from a function. -- -- This version gives you access to the whole Field, which -- includes information about whether quotes were used, and if so which -- ones. mkDecodeWithQuotes :: (Field s -> DecodeValidation e a) -> Decode e s a -- | Build a Decode from a function. -- -- This version gives you access to the whole SpacedField, which -- includes information about spacing both before and after the field, -- and about quotes if they were used. mkDecodeWithSpaces :: (SpacedField s -> DecodeValidation e a) -> Decode e s a -- | promotes a Decode to work on a whole Record at once. This does -- not need to be called by the user. Instead use decode. promote :: Decode' s a -> Record s -> DecodeValidation s a -- | This module exports most of the other modules from the package. It is -- intended to be imported unqualified, along with some qualified imports -- for the Data.Sv.Decode and Data.Sv.Encode modules as -- needed. -- --
--   import Data.Sv
--   import qualified Data.Sv.Decode as D
--   import qualified Data.Sv.Encode as E
--   
module Data.Sv -- | Decodes a sv into a list of its values using the provided -- Decode decode :: Decode' s a -> Sv s -> DecodeValidation s [a] -- | Parse a ByteString as an Sv, and then decode it with the given -- decoder. -- -- This version uses Trifecta to parse the ByteString, -- which is assumed to be UTF-8 encoded. If you want a different library, -- use parseDecode'. parseDecode :: Decode' ByteString a -> ParseOptions ByteString -> ByteString -> DecodeValidation ByteString [a] -- | Parse text as an Sv, and then decode it with the given decoder. -- -- This version lets you choose which parsing library to use by providing -- an SvParser. Common selections are trifecta and -- attoparsecByteString. parseDecode' :: SvParser s -> Decode' s a -> ParseOptions s -> s -> DecodeValidation s [a] -- | Load a file, parse it, and decode it. -- -- This version uses Trifecta to parse the file, which is assumed to be -- UTF-8 encoded. parseDecodeFromFile :: MonadIO m => Decode' ByteString a -> ParseOptions ByteString -> FilePath -> m (DecodeValidation ByteString [a]) -- | Load a file, parse it, and decode it. -- -- This version lets you choose which parsing library to use by providing -- an SvParser. Common selections are trifecta and -- attoparsecByteString. parseDecodeFromFile' :: MonadIO m => SvParser s -> Decode' s a -> ParseOptions s -> FilePath -> m (DecodeValidation s [a]) -- | Build a Decode, given a function that returns Maybe. -- -- Return the given error if the function returns Nothing. decodeMay :: DecodeError e -> (s -> Maybe a) -> Decode e s a -- | Build a Decode, given a function that returns Either. decodeEither :: (s -> Either (DecodeError e) a) -> Decode e s a -- | Build a Decode, given a function that returns Either, -- and a function to build the error. decodeEither' :: (e -> DecodeError e') -> (s -> Either e a) -> Decode e' s a -- | This can be used to build a Decode whose value depends on the -- result of another Decode. This is especially useful since -- Decode is not a Monad. -- -- If you need something like this but with more power, look at -- bindDecode (>>==) :: Decode e s a -> (a -> DecodeValidation e b) -> Decode e s b infixl 1 >>== -- | flipped >>== (==<<) :: (a -> DecodeValidation e b) -> Decode e s a -> Decode e s b infixr 1 ==<< -- | Encode the given list with the given Encode, configured by the -- given EncodeOptions. encode :: Encode a -> EncodeOptions -> [a] -> ByteString -- | Encode, writing to a file. This is way is more efficient than encoding -- to a ByteString and then writing to file. encodeToFile :: Encode a -> EncodeOptions -> [a] -> FilePath -> IO () -- | Encode, writing the output to a file handle. encodeToHandle :: Encode a -> EncodeOptions -> [a] -> Handle -> IO () -- | Encode to a ByteString Builder, which is useful if you are -- going to combine the output with other ByteStrings. encodeBuilder :: Encode a -> EncodeOptions -> [a] -> Builder -- | Encode one row only encodeRow :: Encode a -> EncodeOptions -> a -> ByteString -- | Build an Sv rather than going straight to ByteString. -- This allows you to query the Sv or run sanity checks. encodeSv :: Encode a -> EncodeOptions -> Maybe (NonEmpty ByteString) -> [a] -> Sv ByteString -- | Laws: -- --
--   <!> is associative:             (a <!> b) <!> c = a <!> (b <!> c)
--   <$> left-distributes over <!>:  f <$> (a <!> b) = (f <$> a) <!> (f <$> b)
--   
-- -- If extended to an Alternative then <!> should -- equal <|>. -- -- Ideally, an instance of Alt also satisfies the "left -- distributon" law of MonadPlus with respect to <.>: -- --
--   <.> right-distributes over <!>: (a <!> b) <.> c = (a <.> c) <!> (b <.> c)
--   
-- -- But Maybe, IO, Either a, -- ErrorT e m, and STM satisfy the alternative -- "left catch" law instead: -- --
--   pure a <!> b = pure a
--   
-- -- However, this variation cannot be stated purely in terms of the -- dependencies of Alt. -- -- When and if MonadPlus is successfully refactored, this class should -- also be refactored to remove these instances. -- -- The right distributive law should extend in the cases where the a -- Bind or Monad is provided to yield variations of the -- right distributive law: -- --
--   (m <!> n) >>- f = (m >>- f) <!> (m >>- f)
--   (m <!> n) >>= f = (m >>= f) <!> (m >>= f)
--   
class Functor f => Alt (f :: * -> *) -- | <|> without a required empty () :: Alt f => f a -> f a -> f a some :: (Alt f, Applicative f) => f a -> f [a] many :: (Alt f, Applicative f) => f a -> f [a] -- | The class of contravariant functors. -- -- Whereas in Haskell, one can think of a Functor as containing or -- producing values, a contravariant functor is a functor that can be -- thought of as consuming values. -- -- As an example, consider the type of predicate functions a -> -- Bool. One such predicate might be negative x = x < 0, -- which classifies integers as to whether they are negative. However, -- given this predicate, we can re-use it in other situations, providing -- we have a way to map values to integers. For instance, we can -- use the negative predicate on a person's bank balance to work -- out if they are currently overdrawn: -- --
--   newtype Predicate a = Predicate { getPredicate :: a -> Bool }
--   
--   instance Contravariant Predicate where
--     contramap f (Predicate p) = Predicate (p . f)
--                                            |   `- First, map the input...
--                                            `----- then apply the predicate.
--   
--   overdrawn :: Predicate Person
--   overdrawn = contramap personBankBalance negative
--   
-- -- Any instance should be subject to the following laws: -- --
--   contramap id = id
--   contramap f . contramap g = contramap (g . f)
--   
-- -- Note, that the second law follows from the free theorem of the type of -- contramap and the first law, so you need only check that the -- former condition holds. class Contravariant (f :: * -> *) contramap :: Contravariant f => (a -> b) -> f b -> f a -- | Replace all locations in the output with the same value. The default -- definition is contramap . const, but this may -- be overridden with a more efficient version. (>$) :: Contravariant f => b -> f b -> f a -- | A Divisible contravariant functor is the contravariant analogue -- of Applicative. -- -- Continuing the intuition that Contravariant functors consume -- input, a Divisible contravariant functor also has the ability -- to be composed "beside" another contravariant functor. -- -- Serializers provide a good example of Divisible contravariant -- functors. To begin let's start with the type of serializers for -- specific types: -- --
--   newtype Serializer a = Serializer { runSerializer :: a -> ByteString }
--   
-- -- This is a contravariant functor: -- --
--   instance Contravariant Serializer where
--     contramap f s = Serializer (runSerializer s . f)
--   
-- -- That is, given a serializer for a (s :: Serializer -- a), and a way to turn bs into as (a mapping -- f :: b -> a), we have a serializer for b: -- contramap f s :: Serializer b. -- -- Divisible gives us a way to combine two serializers that focus on -- different parts of a structure. If we postulate the existance of two -- primitive serializers - string :: Serializer String and -- int :: Serializer Int, we would like to be able to combine -- these into a serializer for pairs of Strings and -- Ints. How can we do this? Simply run both serializer and -- combine their output! -- --
--   data StringAndInt = StringAndInt String Int
--   
--   stringAndInt :: Serializer StringAndInt
--   stringAndInt = Serializer $ (StringAndInt s i) ->
--     let sBytes = runSerializer string s
--         iBytes = runSerializer int i
--     in sBytes <> iBytes
--   
-- -- divide is a generalization by also taking a contramap -- like function to split any a into a pair. This conveniently -- allows you to target fields of a record, for instance, by extracting -- the values under two fields and combining them into a tuple. -- -- To complete the example, here is how to write stringAndInt -- using a Divisible instance: -- --
--   instance Divisible Serializer where
--     conquer = Serializer (const mempty)
--   
--     divide toBC bSerializer cSerializer = Serializer $ a ->
--       case toBC a of
--         (b, c) ->
--           let bBytes = runSerializer bSerializer b
--               cBytes = runSerializer cSerializer c
--           in bBytes <> cBytes
--   
--   stringAndInt :: Serializer StringAndInt
--   stringAndInt =
--     divide ((StringAndInt s i) -> (s, i)) string int
--   
class Contravariant f => Divisible (f :: * -> *) divide :: Divisible f => (a -> (b, c)) -> f b -> f c -> f a -- | Conquer acts as an identity for combining Divisible functors. conquer :: Divisible f => f a -- |
--   divided = divide id
--   
divided :: Divisible f => f a -> f b -> f (a, b) -- | A Decidable contravariant functor is the contravariant analogue -- of Alternative. -- -- Noting the superclass constraint that f must also be -- Divisible, a Decidable functor has the ability to "fan -- out" input, under the intuition that contravariant functors consume -- input. -- -- In the dicussion for Divisible, an example was demonstrated -- with Serializers, that turn as into -- ByteStrings. Divisible allowed us to serialize the -- product of multiple values by concatenation. By making our -- Serializer also Decidable- we now have the ability -- to serialize the sum of multiple values - for example different -- constructors in an ADT. -- -- Consider serializing arbitrary identifiers that can be either -- Strings or Ints: -- --
--   data Identifier = StringId String | IntId Int
--   
-- -- We know we have serializers for Strings and Ints, -- but how do we combine them into a Serializer for -- Identifier? Essentially, our Serializer needs to -- scrutinise the incoming value and choose how to serialize it: -- --
--   identifier :: Serializer Identifier
--   identifier = Serializer $ identifier ->
--     case identifier of
--       StringId s -> runSerializer string s
--       IntId i -> runSerializer int i
--   
-- -- It is exactly this notion of choice that Decidable encodes. -- Hence if we add an instance of Decidable for -- Serializer... -- --
--   instance Decidable Serializer where
--     lose f = Serializer $ a -> absurd (f a)
--     choose split l r = Serializer $ a ->
--       either (runSerializer l) (runSerializer r) (split a)
--   
-- -- Then our identifier Serializer is -- --
--   identifier :: Serializer Identifier
--   identifier = choose toEither string int where
--     toEither (StringId s) = Left s
--     toEither (IntId i) = Right i
--   
class Divisible f => Decidable (f :: * -> *) -- | Acts as identity to choose. lose :: Decidable f => (a -> Void) -> f a choose :: Decidable f => (a -> Either b c) -> f b -> f c -> f a -- |
--   chosen = choose id
--   
chosen :: Decidable f => f b -> f c -> f Either b c