data-easy-0.7.0: src/Data/Easy.hs
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE CPP #-}
-- | @easy-data@ aims to make @'Either'@, @'List'@, @'Tuple'@, @'Monoid'@ and
-- @'Bool'@ counterparts to the functions originally defined in
-- "Data.Maybe", whenever applicable.
--
-- This module also adds some extra useful functions, that can be found
-- in otherwise disperse packages, pages, mailing lists, etc.
-- A relevant link will be included whenever appropriate, or just a simple
-- note regarding where to find the other implementations.
-- The main goal is to have a consistent set of sensible convertions
-- between types, providing either default values or custom error messages
-- when faced with partial functions (in a mathematical sense).
--
-- This module is undoubtably neither original, nor providing the \'best\'
-- implementations. Its goal is instead to provide a regular and consistent
-- set of functions, easy do memorize and use, for the Haskell beginner.
--
-- Most functions are one-liners, and you should read their actual code,
-- to either use it as a more idiomatic haskell code, or to develop a
-- better version yourself. Most of these functions are hand-picked from
-- one of the following libraries, that also feature a lot of other goodies,
-- so you should check them out.
--
-- @safe@ : <http://hackage.haskell.org/package/safe>
--
-- @either@ : <http://hackage.haskell.org/package/either>
--
-- @errors@ : <http://hackage.haskell.org/package/errors>
--
-- @basic-prelude@:<http://hackage.haskell.org/package/basic-prelude>
--
-- @missingh@ : <http://hackage.haskell.org/package/MissingH>
--
-- @utility-ht@ : <http://hackage.haskell.org/package/utility-ht>
--
-- Note that the "Safe" module is re-exported by this module.
-- Please notify me if you think I'm missing
-- some other library.
--
-- For monad related functions, check my other related module,
-- "Control.Monad.Trans.Convert", or the modules that inspired it, @either@ and
-- @errors@.
--
-- Some choices have been made, and I am open to discussion whether they
-- are adequate or not. Please contribute and help me make this a (even)
-- more easy and consistent module.
--
module Data.Easy
( -- * Module exports
module Safe
-- * Additional functions
-- ** Maybe
-- | Since this module maps the "Data.Maybe" functions to other data types,
-- we mainly just import (and re-export) this module.
-- The extra functions are dedicated to conversions to other types.
, maybeToMonoid
, monoidToMaybe
-- ** Either
-- | Copied most of the functions from "Data.Either.Combinators",
-- from the "either" package. This package has a huge import list,
-- unnecessary for such simple combinators.
, fromLeft
, fromRight
, fromLeft'
, fromRight'
, mapBoth
, mapLeft
, mapRight
, whenLeft
, whenRight
, unlessLeft
, unlessRight
, leftToMaybe
, rightToMaybe
, fromRightNote
, fromLeftNote
, fromEither
, listToEither
, eitherToList
, catEithers
, mapEither
, maybeToEither
, eitherToMaybe
, eitherToMonoid
, monoidToEither
, joinEitherMonoid
-- ** List
-- | Data.Maybe counterparts for List, plus some extra functions.
-- One special note for 'nubSort': this is the only \'optimized\'
-- function in this library, mainly because the original
-- @nub . sort@ performance is so bad.
-- Nevertheless, never forget that you should probably not be using
-- lists anyhow:
-- <http://www.haskell.org/haskellwiki/Performance>
, list
, isFilled
, notNull
, isNull
, fromHeadNote
, fromList
, catLists
, mapList
, singleton
, mapV
, nubSort
, nubSort'
, atLeast
-- ** Tuple Pairs
-- | Monoid class restriction will be used in tuple elements whenever
-- necessary to create the concept of \'valid\' value.
--
-- Here we adopt the convention of a 'direct' mapping between @'Either'@ and
-- a tuple pair, meaning that the second value of the pair is considered the
-- \'main\' one, whenever applicable. However, if you prefer the first value
-- to be considered instead, you can use the reciprocal \"function'\",
-- like for example 'pairToMaybe''.
--
-- /Note/: if you need real heterogeneous lists, see the "HList" package.
-- <http://hackage.haskell.org/package/HList>
, pair
, pairS
, isPairNotEmpty
, isPairEmpty
, fromFst
, fromSnd
, fromPairNote
, fromPair
, listToPairNote
, listToPairs
, group2
, pairToList
, catPairs
, mapPair
, pairToEither
, pairToEither'
, pairBothToEither
, eitherToPair
, eitherToPair'
, pairToMaybe
, pairToMaybe'
, pairFstToMaybe
, pairSndToMaybe
, maybeToPair
, maybeToPair'
, pairToMonoid
, pairToMonoid'
-- ** Tuple Triples
-- | Monoid class restriction will be used in tuple elements whenever
-- necessary to create the concept of \'valid\' value.
--
-- Since it does not make sense to map a triple to an Either, here we follow
-- a different convention than from pairs, meaning that the first value
-- is always considered the \'valid\' value, if the function needs to
-- choose (the first \'valid\' value).
--
-- /Note/: if you need real heterogeneous lists, see the "HList" package.
-- <http://hackage.haskell.org/package/HList>
--
-- /Note/: we use the postfix ' to distinguish from tuple pairs, for
-- example in the @'snd''@ function. This clearly doesn't scale to bigger tuples.
-- If you need those, you probably should be using a better
-- library than this, no? See <http://hackage.haskell.org/package/lens>.
--
, triple
, tripleS
, isTripleNotEmpty
, isTripleEmpty
, fromFst'
, fst'
, fromSnd'
, snd'
, fromTrd'
, trd'
, fromTripleNote
, fromTriple
, listToTripleNote
, listToTriples
, group3
, tripleToList
, catTriples
, mapTriple
, toFstPairToTriple
, toSndPairToTriple
, toTrdPairToTriple
, pairToTriple
, dropFstTripleToPair
, dropSndTripleToPair
, dropTrdTripleToPair
, tripleToPair
, tripleToMaybe
, tripleToMaybe'
, tripleToMonoid
, tripleToMonoid'
, curry3
, uncurry3
-- ** Monoid
-- | The monoid version of the functions
-- deviate slightly from the others, in the sense no value is extracted
-- from or promoted to a monoid. Instead, the value is just checked against
-- mempty, and kept|discarded|operated on accordingly.
-- See <http://hackage.haskell.org/package/monoid-subclasses> module on
-- hackage for a perhaps saner approach.
, monoid
, isNotEmpty
, notEmpty
, isEmpty
, fromNotEmptyNote
, fromMonoid
, (?+)
, (<!>)
, listToMonoid
, monoidToList
, catMonoids
, nonEmpty
, mapMonoid
, getFirst'
, getLast'
, headF
, lastF
, atF
, (@@)
-- ** Bool
-- | Some extra functions included, namely the simplified ternary operator
-- modified from what is seen in <https://gist.github.com/Burgestrand/218987>
--
-- /Note/: This is probably not considered good practice.
-- Use the standard @if-then-else@ instead, its almost always clearer.
-- /You have been warned./
--
, fromBool
, fromBoolC
, catBools
, (?)
, (?$)
, (?|)
, boolToMaybe
, ifToMaybe
, boolCToMaybe
, ifCToMaybe
, boolToEither
, boolCToEither
, boolToList
, boolCToList
, boolToMonoid
, boolCToMonoid
, (?&&)
, (?&&\)
, allCond
, anyCond
) where
import Data.Maybe
import Data.Either
-- #if __GLASGOW_HASKELL__ >= 707
-- import Data.Either.Combinators hiding (isLeft,isRight)
-- #else
-- import Data.Either.Combinators
-- #endif
import Safe
import qualified Data.List as L
import qualified Data.Set as Set
import qualified Data.Foldable as F
------------------------------------------------------------------------------
-- Maybe ---------------------------------------------------------------------
------------------------------------------------------------------------------
-- | Maybe to monoid conversion
maybeToMonoid :: (Monoid a) => Maybe a -> a
maybeToMonoid Nothing = mempty
maybeToMonoid (Just x) = x
-- | Convert a monoid value into a maybe value (Nothing if mempty).
--
-- > monoidToMaybe = monoid Nothing Just
monoidToMaybe :: (Eq a, Monoid a) => a -> Maybe a
monoidToMaybe = monoid Nothing Just
------------------------------------------------------------------------------
-- Either --------------------------------------------------------------------
------------------------------------------------------------------------------
-- From Data.Either.Combinators ----------------------------------------------
-- ---------------------------------------------------------------------------
-- Functions over Either
-- | Extracts the element out of a 'Left' and
-- throws an error if its argument take the form @'Right' _@.
--
-- Using @Control.Lens@:
--
-- @
-- 'fromLeft'' x ≡ x^?!_Left
-- @
--
-- >>> fromLeft' (Left 12)
-- 12
fromLeft' :: Either a b -> a
fromLeft' (Right _) = error "Data.Easy.fromLeft: Argument takes form 'Right _'" -- yuck
fromLeft' (Left x) = x
-- | Extracts the element out of a 'Right' and
-- throws an error if its argument take the form @'Left' _@.
--
-- Using @Control.Lens@:
--
-- @
-- 'fromRight'' x ≡ x^?!_Right
-- @
--
-- >>> fromRight' (Right 12)
-- 12
fromRight' :: Either a b -> b
fromRight' (Left _) = error "Data.Easy.fromRight: Argument takes form 'Left _'" -- yuck
fromRight' (Right x) = x
-- | The 'mapBoth' function takes two functions and applies the first if iff the value
-- takes the form @'Left' _@ and the second if the value takes the form @'Right' _@.
--
-- Using @Data.Bifunctor@:
--
-- @
-- 'mapBoth' = bimap
-- @
--
-- Using @Control.Arrow@:
--
-- @
-- 'mapBoth' = ('Control.Arrow.+++')
-- @
--
-- >>> mapBoth (*2) (*3) (Left 4)
-- Left 8
--
-- >>> mapBoth (*2) (*3) (Right 4)
-- Right 12
mapBoth :: (a -> c) -> (b -> d) -> Either a b -> Either c d
mapBoth f _ (Left x) = Left (f x)
mapBoth _ f (Right x) = Right (f x)
-- | The 'mapLeft' function takes a function and applies it to an Either value
-- iff the value takes the form @'Left' _@.
--
-- Using @Data.Bifunctor@:
--
-- @
-- 'mapLeft' = first
-- @
--
-- Using @Control.Arrow@:
--
-- @
-- 'mapLeft' = ('Control.Arrow.left')
-- @
--
-- Using @Control.Lens@:
--
-- @
-- 'mapLeft' = over _Left
-- @
--
-- >>> mapLeft (*2) (Left 4)
-- Left 8
--
-- >>> mapLeft (*2) (Right "hello")
-- Right "hello"
mapLeft :: (a -> c) -> Either a b -> Either c b
mapLeft f = mapBoth f id
-- | The 'mapRight' function takes a function and applies it to an Either value
-- iff the value takes the form @'Right' _@.
--
-- Using @Data.Bifunctor@:
--
-- @
-- 'mapRight' = second
-- @
--
-- Using @Control.Arrow@:
--
-- @
-- 'mapRight' = ('Control.Arrow.right')
-- @
--
-- Using @Control.Lens@:
--
-- @
-- 'mapRight' = over _Right
-- @
--
-- >>> mapRight (*2) (Left "hello")
-- Left "hello"
--
-- >>> mapRight (*2) (Right 4)
-- Right 8
mapRight :: (b -> c) -> Either a b -> Either a c
mapRight = mapBoth id
-- | The 'whenLeft' function takes an 'Either' value and a function which returns a monad.
-- The monad is only executed when the given argument takes the form @'Left' _@, otherwise
-- it does nothing.
--
-- Using @Control.Lens@:
--
-- @
-- 'whenLeft' ≡ forOf_ _Left
-- @
--
-- >>> whenLeft (Left 12) print
-- 12
whenLeft :: Applicative m => Either a b -> (a -> m ()) -> m ()
whenLeft (Left x) f = f x
whenLeft _ _ = pure ()
-- | The 'whenRight' function takes an 'Either' value and a function which returns a monad.
-- The monad is only executed when the given argument takes the form @'Right' _@, otherwise
-- it does nothing.
--
-- Using @Data.Foldable@:
--
-- @
-- 'whenRight' ≡ 'forM_'
-- @
--
-- Using @Control.Lens@:
--
-- @
-- 'whenRight' ≡ forOf_ _Right
-- @
--
-- >>> whenRight (Right 12) print
-- 12
whenRight :: Applicative m => Either a b -> (b -> m ()) -> m ()
whenRight (Right x) f = f x
whenRight _ _ = pure ()
-- | A synonym of 'whenRight'.
unlessLeft :: Applicative m => Either a b -> (b -> m ()) -> m ()
unlessLeft = whenRight
-- | A synonym of 'whenLeft'.
unlessRight :: Applicative m => Either a b -> (a -> m ()) -> m ()
unlessRight = whenLeft
-- | Extract the left value or a default.
--
-- @
-- 'fromLeft' ≡ 'either' 'id'
-- @
--
-- >>> fromLeft "hello" (Right 42)
-- "hello"
--
-- >>> fromLeft "hello" (Left "world")
-- "world"
fromLeft :: a -> Either a b -> a
fromLeft _ (Left x) = x
fromLeft x _ = x
-- | Extract the right value or a default.
--
-- @
-- 'fromRight' b ≡ 'either' b 'id'
-- @
--
-- >>> fromRight "hello" (Right "world")
-- "world"
--
-- >>> fromRight "hello" (Left 42)
-- "hello"
fromRight :: b -> Either a b -> b
fromRight _ (Right x) = x
fromRight x _ = x
-- | Maybe get the 'Left' side of an 'Either'.
--
-- @
-- 'leftToMaybe' ≡ 'either' 'Just' ('const' 'Nothing')
-- @
--
-- Using @Control.Lens@:
--
-- @
-- 'leftToMaybe' ≡ preview _Left
-- 'leftToMaybe' x ≡ x^?_Left
-- @
--
-- >>> leftToMaybe (Left 12)
-- Just 12
--
-- >>> leftToMaybe (Right 12)
-- Nothing
leftToMaybe :: Either a b -> Maybe a
leftToMaybe = either Just (const Nothing)
-- | Maybe get the 'Right' side of an 'Either'.
--
-- @
-- 'rightToMaybe' ≡ 'either' ('const' 'Nothing') 'Just'
-- @
--
-- Using @Control.Lens@:
--
-- @
-- 'rightToMaybe' ≡ preview _Right
-- 'rightToMaybe' x ≡ x^?_Right
-- @
--
-- >>> rightToMaybe (Left 12)
-- Nothing
--
-- >>> rightToMaybe (Right 12)
-- Just 12
rightToMaybe :: Either a b -> Maybe b
rightToMaybe = either (const Nothing) Just
-- New functions / Renamed functions -----------------------------------------
-- | Force a right value, or otherwise fail with provided error message
--
-- > fromRightNote err = either (error err) id
fromRightNote :: String -> Either a b -> b
fromRightNote err = either (error err) id
-- | Force a left value, or otherwise fail with provided error message
--
-- > fromLeftNote err = either id (error err)
fromLeftNote :: String -> Either a b -> a
fromLeftNote err = either id (error err)
-- | Force a right value, providing a default value if the Either is Left
fromEither :: b -> Either a b -> b
fromEither _ (Right x) = x
fromEither x _ = x
-- | Extract the first element of a list as a Right value, or else use the
-- default value provided as a Left value
listToEither :: a -> [b] -> Either a b
listToEither def = maybeToEither def . listToMaybe
-- | Extracts the right value of an either to a singleton list, or an
-- empty list if the Either value is a Left
--
-- /Note/: A Left value is lost in the convertion.
eitherToList :: Either a b -> [b]
eitherToList = either (const []) singleton
-- | The 'catEithers' function takes a list of 'Either's and returns
-- a list of all the 'Right' values.
--
-- This is just an alias for 'rights', defined in @Data.Either@
--
-- @
-- 'catEithers' = 'rights'
-- @
--
catEithers :: [Either a b] -> [b]
catEithers = rights
-- | The 'mapEither' function is a version of 'map' which can throw
-- out elements. In particular, the functional argument returns
-- something of type @'Either' a b@. If this is 'Left a', no element
-- is added on to the result list. If it just @'Right' b@, then @b@ is
-- included in the result list.
--
-- > mapEither f = rights . map f
mapEither :: (a -> Either b c) -> [a] -> [c]
mapEither f = catEithers . map f
-- | Convert a Maybe value to an Either value, with the provided default used
-- as Left value if the Maybe value is Nothing
maybeToEither :: a -> Maybe b -> Either a b
maybeToEither def = maybe (Left def) Right
-- | Convert an Either value to a Maybe value
--
-- This function is provided with a different name convention on
-- @Data.Either.Combinators@:
--
-- @
-- 'eitherToMaybe' = 'rightToMaybe'
-- @
--
eitherToMaybe :: Either a b -> Maybe b
eitherToMaybe = either (const Nothing) Just
-- | eitherToMonoid extract the right sided monoid into a single monoid
-- value, or mempty in the case of a left value.
--
-- > eitherToMonoid = either mempty id
eitherToMonoid :: (Monoid b) => Either a b -> b
eitherToMonoid = either mempty id
-- | monoidToEither extracts a non-empty value to the right side, or
-- otherwise fills the 'Left' side with the provided value.
monoidToEither :: (Eq b, Monoid b) => a -> b -> Either a b
monoidToEither def = monoid (Left def) Right
-- | Case analysis for a either monoid. If the right side of the monoid
-- is @'mempty'@, then the value is transformed to a left value, using
-- the provided function.
joinEitherMonoid :: (Eq b, Monoid b) => a -> Either a b -> Either a b
joinEitherMonoid emptErr = either Left (monoidToEither emptErr)
------------------------------------------------------------------------------
-- Lists ---------------------------------------------------------------------
------------------------------------------------------------------------------
-- | Apply a function to a non-empty list, and retrieve its result
-- or the default provided value if the list is empty.
list :: b -> ([a] -> b) -> [a] -> b
list def f lst = if null lst then def else f lst
-- could be list = monoid, but would have to add Eq restriction
-- | Alias for @'not'@.@'null'@
{-# INLINE isFilled #-}
isFilled :: [a] -> Bool
isFilled = not . null
-- | Alias for 'not' . 'null' . Yeah, it saves 3 characters.
--
-- > notNull = not . null
{-# INLINE notNull #-}
notNull :: [a] -> Bool
notNull = not . null
-- | Alias for null
{-# INLINE isNull #-}
isNull :: [a] -> Bool
isNull = null
-- | Similar to @'headNote'@ from "Safe" package
-- However, no text is added to the provided string error,
-- for more deterministic error messages transparency.
fromHeadNote :: String -> [a] -> a
fromHeadNote err [] = error err
fromHeadNote _ (x:_) = x
-- | Returns the first value of a list if not empty, or the
-- provided default value if the list is empty
fromList :: a -> [a] -> a
fromList = headDef
-- | Alias for @'catMonoid'@.
catLists :: (Eq a) => [[a]] -> [[a]]
catLists = catMonoids
-- | Alias for concatMap
mapList :: (a -> [b]) -> [a] -> [b]
mapList = concatMap
-- | Insert a single value into a list
--
-- > singleton = return
--
-- or
--
-- > singleton = (:[])
{-# INLINE singleton #-}
singleton :: a -> [a]
singleton = return
-- | map a value over a list of functions, and return a list of values
--
-- /See/: <http://www.haskell.org/pipermail/haskell-cafe/2007-February/022694.html>
--
-- @
-- /Alternative 1/: mapV value = map ($ value)
-- @
--
-- @
-- /Alternative 2/: mapV value lst = sequence lst value
-- @
--
mapV :: a -> [a -> b] -> [b]
mapV value = map ($ value)
-- | Sort and nub (remove duplicates) from a list.
-- Specially for large lists, this is much more efficient than @nub . sort@.
--
-- /Note/: You shold probably be using "Data.Set".
--
-- > nubSort = Set.toAscList . Set.fromList
--
nubSort :: (Ord a) => [a] -> [a]
nubSort = Set.toAscList . Set.fromList
-- | Sort, nub (remove duplicates) and remove initial empty value, if it
-- exists. See 'nubSort'.
nubSort' :: (Ord a, Monoid a) => [a] -> [a]
nubSort' lst = case nubSort lst of
[] -> []
(x:xs) -> if isEmpty x then xs else x:xs
-- | Lazy length: determine if a list has a given size without computing
-- all of its elements.
--
-- See <http://www.haskell.org/haskellwiki/Haskell_programming_tips>
atLeast :: Int -> [a] -> Bool
atLeast 0 _ = True
atLeast _ [] = False
atLeast n (_:ys) = atLeast (n-1) ys
------------------------------------------------------------------------------
-- Tuple Pair ----------------------------------------------------------------
-- Monoid class restriction will be used when applicable ---------------------
------------------------------------------------------------------------------
-- | Case evaluation for a tuple pair, 'reducing' it to a single value
pair :: (Monoid c) => (a -> c) -> (b -> c) -> (a,b) -> c
pair f g (a,b) = f a `mappend` g b
-- | Case evaluation for single type tuple pairs, simplification of 'pair'.
pairS :: (Monoid b) => (a -> b) -> (a,a) -> b
pairS f (a,b) = f a `mappend` f b
-- | Is the pair tuple \'valid\', i.e., does it have at least one
-- non-empty (monoid) value?
isPairNotEmpty :: (Eq a, Monoid a, Eq b, Monoid b) => (a, b) -> Bool
isPairNotEmpty (x,y) = isNotEmpty x || isNotEmpty y
--
-- | Is the pair tuple \'invalid\', i.e., are both (monoid) elements
-- 'mempty'?
isPairEmpty :: (Eq a, Monoid a, Eq b, Monoid b) => (a, b) -> Bool
isPairEmpty (x,y) = isEmpty x && isEmpty y
-- | Longer (??) alias for 'fst'.
--
-- /Note/: included just for \'consistency\' with the rest of the API.
-- Use @'fst'@.
fromFst :: (a,b) -> a
fromFst = fst
-- | Longer (??) alias for 'snd'.
fromSnd :: (a,b) -> b
--
-- /Note/: included just for \'consistency\' with the rest of the API.
-- Use @'snd'@.
fromSnd = snd
-- | 'mappend' the two monoid elements of a pair
fromPairNote :: (Eq a, Monoid a) => String -> (a,a) -> a
fromPairNote err (a,b) = fromMonoid (error err) (a `mappend` b)
-- | 'mappend' the two monoid elements of a pair
fromPair :: (Eq a, Monoid a) => a -> (a,a) -> a
fromPair def (a,b) = fromMonoid def (a `mappend` b)
-- | listToPair grabs the two first elements of a list, and inserts them
-- into a tuple. If not enough elements are available, raise the provided
-- error.
listToPairNote :: String -> [a] -> (a,a)
listToPairNote err [] = error err
listToPairNote err (_:[]) = error err
listToPairNote _ (x:y:_) = (x,y)
-- | Groups the elements of a list two by two, also returning the (possible)
-- unpaired item not grouped.
listToPairs :: [a] -> ([(a,a)],[a])
listToPairs [] = ([],[])
listToPairs (x:[]) = ([],[x])
listToPairs (x0:x1:xs) = ( (x0,x1):(fst . listToPairs $ xs)
, snd . listToPairs $ xs)
-- | Similar to 'listToPairs', but discards the (possible) unpaired item.
group2 :: [a] -> [(a,a)]
group2 = fst . listToPairs
-- | Convert a single type pair into a two elements list
pairToList :: (a,a) -> [a]
pairToList (a,b) = [a,b]
-- | 'mappend' each pair in a list into a single value, and filter out
-- 'mempty' values
catPairs :: (Eq a, Monoid a) => [(a,a)] -> [a]
catPairs = mapMonoid (fromPair mempty)
-- | Applies a pair returning function to each list element,
-- and keeps only the non-empty mappend results (between the pair elements).
mapPair :: (Eq b, Monoid b) => (a -> (b,b)) -> [a] -> [b]
mapPair f = catPairs . fmap f
-- | Transform a pair into an either.
-- We adopt the convention that the second value is the one of interest.
-- It is matched against @'mempty'@, and if equal the first value is returned
-- as a @'Left'@ value.
pairToEither :: (Eq b, Monoid b) => (a,b) -> Either a b
pairToEither (a,b) = if isEmpty b then Left a else Right b
-- | Transform a pair into an either.
-- The same as 'pairToEither', but the first tuple element is considered.
pairToEither' :: (Eq a, Monoid a) => (a,b) -> Either b a
pairToEither' (a,b) = if isEmpty a then Left b else Right a
-- | Transform a pair into an either.
-- Both values are checked for a valid monoid (non-empty).
-- The first to be found is returned as a Right value.
-- If none is found, a default value is returned.
pairBothToEither :: (Eq a, Monoid a) => b -> (a,a) -> Either b a
pairBothToEither def (a,b) = monoid (monoid (Left def) Right b) Right a
-- | Transform an @'Either'@ value into a pair. This follows the same
-- convention as @'pairToEither'@, and thus transforms a @'Left' value@
-- into a @('Left' value,'mempty')@, and a @'Right' value@ into a @(def, value)@.
eitherToPair :: Monoid b => a -> Either a b -> (a, b)
eitherToPair def = either (\lft -> (,) lft mempty) ((,) def)
-- | Transform an @'Either'@ value into a pair. This follows the same
-- convention as @'pairToEither''@, and thus transforms a @'Left' value@
-- into a @('mempty', 'Left' value)@, and a @'Right' value@ into a @(value, def)@.
eitherToPair' :: Monoid a => b -> Either b a -> (a, b)
eitherToPair' def = either ((,) mempty) (\rgt -> (,) rgt def)
-- | Transform a pair onto a @'Maybe'@
-- This function follows the same convention as @'pairToEither'@, and thus
-- the second value is considered the most important one, and as such
-- will take precedence over the first if both are not empty.
-- If you prefer the first value to take precedence, see @'pairToMaybe''@.
-- If both elements of the pair are @'mempty'@, this function returns @'Nothing'@.
--
-- /Note/: the reciprocal of this function is @'pairToMaybe'@.
--
-- > pairToMaybe = monoid (monoid Nothing Just a) Just b
pairToMaybe :: (Eq a, Monoid a) => (a,a) -> Maybe a
pairToMaybe (a,b) = monoid (monoid Nothing Just a) Just b
-- | Transform a pair onto a @'Maybe'@
-- If both the values are non-empty, the first one is returned wrapped in
-- a Just. If just one value is not-empty, that value is returned,
-- irrespectively if it is the first or second.
-- Otherwise, this function returns Nothing.
--
-- /Note/: the reciprocal of this function is @'pairToMaybe'@.
--
-- > pairToMaybe' = monoid (monoid Nothing Just b) Just a
pairToMaybe' :: (Eq a, Monoid a) => (a,a) -> Maybe a
pairToMaybe' (a,b) = monoid (monoid Nothing Just b) Just a
-- | Transform the first element of a pair (if it is a monoid) into an @'Maybe'@.
-- Reciprocal to @'pairSndToMaybe'@.
--
-- > pairToMaybe' = monoitToMaybe . fst
pairFstToMaybe :: (Eq a, Monoid a) => (a,b) -> Maybe a
pairFstToMaybe = monoidToMaybe . fst
-- | Transform the second element of a pair (if it is a monoid) into a @'Maybe'@.
-- Reciprocal to @'pairFstToMaybe'@.
--
-- > pairToMaybe = monoitToMaybe . snd
pairSndToMaybe :: (Eq b, Monoid b) => (a,b) -> Maybe b
pairSndToMaybe = monoidToMaybe . snd
-- | Transform a @'Maybe'@ value into a pair. This follows the same
-- convention as @'pairToMaybe'@, and thus transforms a @'Nothing'@
-- into a @(def, 'mempty')@, and a @'Just' value@ into a @(def, value)@.
--
maybeToPair :: Monoid b => a -> Maybe b -> (a, b)
maybeToPair def = maybe (def,mempty) ((,) def)
-- | Transform a @'Maybe'@ value into a pair. This follows the same
-- convention as @'pairToMaybe''@, and thus transforms a @'Nothing'@
-- into a @('mempty', def)@, and a @'Just' value@ into a @(value,def)@.
--
maybeToPair' :: Monoid a => b -> Maybe a -> (a, b)
maybeToPair' def = maybe (mempty,def) (\jst -> (,) jst def)
-- | Finds the first non-empty monoid in a pair, and returns it.
-- If none found, returns @'mempty'@.
--
-- /Note/: reciprocal to @'pairToMonoid''@
pairToMonoid :: (Eq a, Monoid a) => (a,a) -> a
pairToMonoid (a,b) = fromMonoid (fromMonoid mempty b) a
-- | Finds the last non-empty monoid in a pair, and returns it.
-- If none found, returns @'mempty'@.
pairToMonoid' :: (Eq a, Monoid a) => (a,a) -> a
pairToMonoid' (a,b) = fromMonoid (fromMonoid mempty a) b
------------------------------------------------------------------------------
-- Tuple Triple --------------------------------------------------------------
-- Monoid class restriction will be used when applicable ---------------------
------------------------------------------------------------------------------
-- | Case evaluation for a tuple triple, 'reducing' it to a single value
triple :: (Monoid d) => (a -> d) -> (b -> d) -> (c -> d) -> (a,b,c) -> d
triple f g h (a,b,c) = mconcat [f a, g b, h c]
-- | Case evaluation for single type tuple triples, simplification of 'triple'.
tripleS :: (Monoid b) => (a -> b) -> (a,a,a) -> b
tripleS f (a,b,c) = mconcat [f a, f b, f c]
-- | Is the triple tuple \'valid\', i.e., does it have at least one
-- non-empty (monoid) value?
isTripleNotEmpty
:: (Eq a, Monoid a, Eq b, Monoid b, Eq c, Monoid c)
=> (a, b, c)
-> Bool
isTripleNotEmpty (x,y,z)
= isNotEmpty x || isNotEmpty y || isNotEmpty z
--
-- | Is the pair tuple \'invalid\', i.e., are both (monoid) elements
-- 'mempty'?
isTripleEmpty
:: (Eq a, Monoid a, Eq b, Monoid b, Eq c, Monoid c)
=> (a, b, c)
-> Bool
isTripleEmpty (x,y,z)
= isEmpty x && isEmpty y && isEmpty z
-- | Extract the first element from a triple
fromFst' :: (a,b,c) -> a
fromFst' (a,_,_) = a
-- | Alias for fromFst' (extract the first element of a triple).
fst' :: (a, b, c) -> a
fst' = fromFst'
-- | Extract the second element from a triple
fromSnd' :: (a,b,c) -> b
fromSnd' (_,a,_) = a
-- | Alias for fromSnd' (extract the second element of a triple).
snd' :: (a, b, c) -> b
snd' = fromSnd'
-- | Extract the third element from a triple
fromTrd' :: (a,b,c) -> c
fromTrd' (_,_,a) = a
-- | Alias for 'fromTrd''
trd' :: (a,b,c) -> c
trd' = fromTrd'
-- | 'mappend' the two monoid elements of a pair
fromTripleNote :: (Eq a, Monoid a) => String -> (a,a,a) -> a
fromTripleNote err (a,b,c) = fromMonoid (error err)
(a `mappend` b `mappend` c)
-- | 'mappend' the three monoid elements of a triple
fromTriple :: (Eq a, Monoid a) => a -> (a,a,a) -> a
fromTriple def (a,b,c) = fromMonoid def (mconcat [a,b,c])
-- | listToTriple grabs the two three elements of a list, and inserts them
-- into a triple tuple. If not enough elements are available, raise the
-- provided error.
listToTripleNote :: String -> [a] -> (a,a,a)
listToTripleNote err [] = error err
listToTripleNote err (_:[]) = error err
listToTripleNote err (_:_:[])= error err
listToTripleNote _ (x:y:z:_) = (x,y,z)
-- | Groups the elements of a list three by three, also returning the
-- (possible) remaining item(s) (not grouped).
listToTriples :: [a] -> ([(a,a,a)],[a])
listToTriples [] = ([],[])
listToTriples (x:[]) = ([],[x])
listToTriples (x0:x1:[])= ([],[x0,x1])
listToTriples (x0:x1:x2:xs)
= ( (x0,x1,x2):(fst . listToTriples $ xs)
, snd . listToTriples $ xs)
-- | Similar to 'listToTriples', but discards the (possible) remaining item(s).
group3 :: [a] -> [(a,a,a)]
group3 = fst . listToTriples
-- | Convert a single type triple tuple into a three elements list
tripleToList :: (a,a,a) -> [a]
tripleToList (a,b,c) = [a,b,c]
-- | 'mappend' each triple in a list into a single value, and filter out
-- 'mempty' values
catTriples :: (Eq a, Monoid a) => [(a,a,a)] -> [a]
catTriples = mapMonoid (fromTriple mempty)
-- TODO
-- | Apply the provided function to each list element resulting in a triple,
-- and keep only the non-empty monoids concat results.
mapTriple :: (Eq b, Monoid b) => (a -> (b,b,b)) -> [a] -> [b]
mapTriple f = catTriples . fmap f
-- | Pair to Triple, inserting the missing element in first place
--
-- > toFstPairToTriple x (y,z) = (x,y,z)
toFstPairToTriple :: a -> (b, c) -> (a, b, c)
toFstPairToTriple x (y, z) = (x, y, z)
-- | Pair to Triple, inserting the missing element in second place
--
-- > toSndPairToTriple y (x, z) = (x, y, z)
toSndPairToTriple :: b -> (a, c) -> (a, b, c)
toSndPairToTriple y (x, z) = (x, y, z)
-- | Pair to Triple, inserting the missing element in third place
--
-- > toTrdPairToTriple z (x, y) = (x, y, z)
toTrdPairToTriple :: c -> (a, b) -> (a, b, c)
toTrdPairToTriple z (x, y) = (x, y, z)
-- | Alias for toTrdPairToTriple
pairToTriple :: c -> (a, b) -> (a, b, c)
pairToTriple = toTrdPairToTriple
-- | Triple to pair, removing the first element.
--
-- > \(_,y,z) -> (y,z)
dropFstTripleToPair :: (a,b,c) -> (b,c)
dropFstTripleToPair (_,y,z) = (y,z)
-- | Triple to pair, removing the second element.
--
-- > \(x,_,z) -> (x,z)
dropSndTripleToPair :: (a,b,c) -> (a,c)
dropSndTripleToPair (x,_,z) = (x,z)
-- | Triple to pair, removing the third element.
--
-- > \(x,y,_) -> (x,y)
dropTrdTripleToPair :: (a,b,c) -> (a,b)
dropTrdTripleToPair (x,y,_) = (x,y)
-- | Alias for 'dropTrdTripleToPair'.
tripleToPair :: (a, b, c) -> (a, b)
tripleToPair = dropTrdTripleToPair
-- | Triple to Maybe. Analogous to @'pairToMaybe'@, it keeps the first
-- non-empty monoid value.
tripleToMaybe :: (Eq a, Monoid a) => (a,a,a) -> Maybe a
tripleToMaybe (a,b,c)
= monoid (monoid (monoid Nothing Just c) Just b) Just a
-- | Triple to Maybe. Analogous to @'pairToMaybe''@, it keeps the last
-- non-empty monoid value.
tripleToMaybe' :: (Eq a, Monoid a) => (a,a,a) -> Maybe a
tripleToMaybe' (a,b,c)
= monoid (monoid (monoid Nothing Just a) Just b) Just c
-- | Triple to Monoid. Analogous to @'pairToMonoid'@, it keeps the first
-- non-empty monoid value.
tripleToMonoid :: (Eq a, Monoid a) => (a,a,a) -> a
tripleToMonoid (a,b,c)
= fromMonoid (fromMonoid (fromMonoid mempty c) b) a
-- | Triple to Maybe. Analogous to @'pairToMonoid''@, it keeps the last
-- non-empty monoid value.
tripleToMonoid' :: (Eq a, Monoid a) => (a,a,a) -> a
tripleToMonoid' (a,b,c)
= fromMonoid (fromMonoid (fromMonoid mempty a) b) c
curry3 :: ((a, b, c) -> d) -> a -> b -> c -> d
curry3 f a b c = f (a,b,c)
{-# INLINE curry3 #-}
uncurry3 :: (a -> b -> c -> d) -> ((a, b, c) -> d)
uncurry3 f ~(a,b,c) = f a b c
{-# INLINE uncurry3 #-}
------------------------------------------------------------------------------
-- Monoid --------------------------------------------------------------------
------------------------------------------------------------------------------
-- | Apply a function to a non-empty monoid, and retrieve its result
-- or the default provided value if the monoid is mempty.
monoid :: (Monoid a, Eq a) => b -> (a -> b) -> a -> b
monoid def f mon = if isEmpty mon then def else f mon
-- | Check that a monoid is not mempty
{-# INLINE isNotEmpty #-}
isNotEmpty :: (Monoid a, Eq a) => a -> Bool
isNotEmpty = (/=) mempty
-- | Alias for @'isNotEmpty'@.
{-# INLINE notEmpty #-}
notEmpty :: (Monoid a, Eq a) => a -> Bool
notEmpty = isNotEmpty
-- | Check it is mempty
{-# INLINE isEmpty #-}
isEmpty :: (Monoid a, Eq a) => a -> Bool
isEmpty = (==) mempty
-- | fromNotEmptyNote keeps the monoid value if it is not empty,
-- otherwise it raises an error with the provided message.
--
-- /Note/: This differs from @fromJust@ in the sense it is not possible
-- to extract values from monoid
fromNotEmptyNote :: (Eq a, Monoid a) => String -> a -> a
fromNotEmptyNote err mon = if isEmpty mon then error err else mon
-- | fromMonoid keeps the monoid value if it is not empty,
-- otherwise it replaces it with the provided default value
--
-- /Note/: No check is made to see if default value is itself mempty
--
-- /Note/: This differs from @fromMaybe@ in the sense it is not possible
-- to extract values from monoid
--
-- /Note/: similar to @flip <|>@ for the appropriate types.
--
fromMonoid :: (Eq a, Monoid a) => a -> a -> a
fromMonoid def mon = if isEmpty mon then def else mon
-- | Infix fromMonoid. Equivalent to higher order ternary operator,
-- similar to python @if@ in expressions
--
-- Example usage:
--
-- > let x = valueThatCanBeEmpty ?+ defaultValue
infixr 1 ?+
(?+) :: (Eq a, Monoid a) => a -> a -> a
mon ?+ def = fromMonoid def mon
-- | Monoid choice operator.
-- See (obligatory reading, even if you don't understand it at first):
-- <http://stackoverflow.com/questions/13080606/confused-by-the-meaning-of-the-alternative-type-class-and-its-relationship-to>
--
-- This operator implements 'Alternative' like choice operator to 'Monoid's.
--
infixl 3 <!>
(<!>) :: (Eq a, Monoid a) => a -> a -> a
mon0 <!> mon1 = if isEmpty mon0 then mon1 else mon0
-- | listToMonoid extracts the first element from a monoid list
-- into a single monoid, or returns mempty if the list is empty
--
-- /Note/: This differs from @listToMaybe@ in the sense it is not possible
-- to promote values into a monoid
--
-- > listToMonoid = headDef mempty
listToMonoid :: (Monoid a) => [a] -> a
listToMonoid = headDef mempty
-- | monoidToList convert an empty monoid into an empty list,
-- otherwise it creates a singleton list with the monoid inside
--
-- /Note/: This differs from @maybeToList@ in the sense it is not possible
-- to extract the value from a monoid
--
-- > monoidToList = monoid [] singleton
monoidToList :: (Eq a, Monoid a) => a -> [a]
monoidToList = monoid [] singleton
-- | Filter out all empty monoids from a list.
--
-- > catMonoids = filter isNotEmpty
catMonoids :: (Eq a, Monoid a) => [a] -> [a]
catMonoids = filter isNotEmpty
-- | Alias for @'catMonoids'@.
nonEmpty :: (Eq a, Monoid a) => [a] -> [a]
nonEmpty = catMonoids
-- | Apply a function that returns a monoid to all elements of a list
-- and return a new list with only not mempty results.
--
-- /Note/: This differs from @mapMaybe@ in the sense it is not possible
-- to extract the value from a monoid.
mapMonoid :: (Eq b, Monoid b) => (a -> b) -> [a] -> [b]
mapMonoid _ [] = []
mapMonoid f (x:xs) =
let rs = mapMonoid f xs in
if isEmpty (f x) then rs else f x:rs
-- | Get the first non-empty element from a list. If all elements are 'mempty',
-- or the list is empty, it returns 'mempty'.
-- /Note/: A newtype based solution as done by maybe in "Data.Monoid" will
-- always be more efficient than this, so this is not really recommend.
-- However, it might come handy in some non-critical code.
getFirst' :: (Eq a, Monoid a) => [a] -> a
getFirst' = maybeToMonoid . L.find isNotEmpty
-- | Get the last non-empty element from a list. If all elements are 'mempty',
-- or the list is empty, it returns 'mempty'.
-- /Note/: A newtype based solution as done by maybe in "Data.Monoid" will
-- always be more efficient than this, so this is not really recommend.
-- However, it might come handy in some non-critical code.
getLast' :: (Eq a, Monoid a) => [a] -> a
getLast' = foldr (\x a -> if isNotEmpty a then a else x) mempty
-- | A @'head'@ that fails returning @'mempty'@.
-- Gets the first element of a foldable stucture of monoids.
--
-- Returns @'mempty'@ if the structure is empty.
headF :: (F.Foldable t, Monoid a) => t a -> a
headF = headDef mempty . F.toList
-- | A @'last'@ that fails returning @'mempty'@.
-- Gets the last element of a foldable stucture of monoids.
-- Returns @'mempty'@ if the structure is empty.
--
-- /Note/: this function starts by mapping the foldable structure to a list...
lastF :: (F.Foldable t, Monoid a) => t a -> a
lastF = lastDef mempty . F.toList
-- | A @'(!!)'@ that fails returning @'mempty'@.
--
-- /Note/: this function starts by mapping the foldable structure to a list...
atF :: (F.Foldable t, Monoid a) => t a -> Int -> a
atF lst index = flip (atDef mempty) index . F.toList $ lst
-- | Infix version of @'atF'@.
infixl 9 @@
(@@) :: (F.Foldable t, Monoid a) => t a -> Int -> a
lst @@ index = flip (atDef mempty) index . F.toList $ lst
------------------------------------------------------------------------------
-- Boolean -------------------------------------------------------------------
------------------------------------------------------------------------------
-- | fromBool is a 'if' rewrite following the call convention of fromMaybe.
fromBool :: a -> Bool -> a -> a
fromBool def b value = if b then value else def
-- | fromBoolC is similar to 'fromBool', but it takes a condition rather
-- than a simple boolean value
fromBoolC :: a -> (a -> Bool) -> a -> a
fromBoolC def f value = if f value then value else def
-- | Cat bools. Filter out False values from a list. Probably useless.
--
-- > catBools = filter id
catBools :: [Bool] -> [Bool]
catBools = filter id
-- catBoolsC == filter
-- | Ternary operator. Use like this:
--
-- > (i > 0) ? i $ 1
--
-- /Note/: this is non-idiomatic haskell. Use at your own risk.
--
-- /Note/: this may require additional parenthesis, so it may not be worth it.
infixr 1 ?
(?) :: Bool -> a -> a -> a
True ? x = const x
False ? _ = id
-- | Higher order ternary operator. Use like this:
--
-- > (not . null) ?$ "" $ "default value"
--
-- /Note/: this is non-idiomatic haskell. Use at your own risk.
infixr 1 ?$
(?$) :: (a -> Bool) -> a -> a -> a
f ?$ x = f x ? x
-- | Higher order ternary operator, similar to python @if@ in expressions.
-- Use like this:
--
-- > "" ?| (not . null) $ "default value"
--
-- /Note/: this is non-idiomatic haskell. Use at your own risk.
infixr 1 ?|
(?|) :: a -> (a -> Bool) -> a -> a
x ?| f = f x ? x
-- | Provided a default value, apply it to a maybe if the predicate holds
boolToMaybe :: a -> Bool -> Maybe a
boolToMaybe value cond = if cond then Just value else Nothing
-- | Same as boolToMaybe, but with a more familiar 'if-like' syntax
ifToMaybe :: Bool -> a -> Maybe a
ifToMaybe = flip boolToMaybe
-- | Test a value with a function returning a Bool, and apply it to a Maybe
-- accordingly.
boolCToMaybe :: a -> (a -> Bool) -> Maybe a
boolCToMaybe value f = if f value then Just value else Nothing
-- | Same as boolCToMaybe, but with a more familiar 'if-like' syntax
ifCToMaybe :: (a -> Bool) -> a -> Maybe a
ifCToMaybe = flip boolCToMaybe
-- | Provided two values, choose amongst them based on a 'Bool' value.
--
-- > \l r b = if b then Right r else Left l
boolToEither :: a -> b -> Bool -> Either a b
boolToEither l r b = if b then Right r else Left l
-- | Provided two values, choose amongst them based on a the provided
-- test on the second value.
--
-- > \l r f = if f r then Left l else Right r
boolCToEither :: a -> b -> (b -> Bool) -> Either a b
boolCToEither l r f = if f r then Right r else Left l
-- | Insert the provided value into a list if the 'Bool' value is 'True',
-- otherwise return an empty list.
boolToList :: a -> Bool -> [a]
boolToList value b = fromBool [] b [value]
-- | Insert the provided value into a list if the provided condition is
-- 'True', otherwise return an empty list.
--
-- Use a list comprehension instead:
--
-- > [value | f value]
boolCToList :: a -> (a -> Bool) -> [a]
boolCToList value f = [value | f value]
-- | Keep the provided value if the 'Bool' value is 'True', 'mempty'
-- otherwise.
boolToMonoid :: (Monoid a) => a -> Bool -> a
boolToMonoid value b = if b then value else mempty
-- | Keep the provided value if the 'Bool' value is 'True', 'mempty'
-- otherwise.
boolCToMonoid :: (Monoid a) => a -> (a -> Bool) -> a
boolCToMonoid value f = if f value then value else mempty
-- | Emulates @and@,@&&@ and @or@,@||@ from scripting languages like python,
-- in the sense you can mix booleans with a value to get the value when
-- the boolean is true (or 'mempty' otherwise).
--
-- However, in order to allow several @'?&&'@ in a row, the order
-- is not the one normally used in languages like bash, where the test comes
-- first.
--
-- /Usage/:
--
-- > value ?&& bool1 ?&& bool2 ?&& ...
--
-- /Note/: this is non-idiomatic haskell. Use at your own risk.
-- You should instead use the following code :
--
-- > if bool1 && bool2 && ... then value else mempty
--
-- Or better yet:
--
-- > if and [bool1,bool2,...] then value else mempty
infixl 1 ?&&
(?&&) :: (Monoid a) => a -> Bool -> a
(?&&) = boolToMonoid
-- | Emulates @and@,@&&@ and @or@,@||@ from scripting languages like python,
-- in the sense you can mix boolean tests with a value to get the original
-- value when all the tests return true (or 'mempty' otherwise).
--
-- However, in order to allow several @'??&&'@ in a row, the order
-- is not the one normally used in languages like bash, where the test comes
-- first.
--
-- /Note/: an easy mnemonic to remember is that operators ending in \\ (lambda)
-- imply that their parameters are functions instead of values (in this particular
-- case, boolean tests)
--
-- /Usage/:
--
-- > value ?&&\ condition1 ?&&\ condition2 ?&&\ ...
--
-- /Note/: this is non-idiomatic haskell. Use at your own risk.
(?&&\) :: (Monoid a) => a -> (a -> Bool) -> a
(?&&\) value f = if f value then value else mempty
infixl 1 ?&&\{-This comment teaches CPP correct behaviour -}
-- ?||
-- Just use: ?&& value (bool1 || bool2 || ... )
-- or: ?&& value $ or [bool1,bool2,...]
-- ?$||
-- Just use: anyCond value [cond1, cond2,...]
-- | Apply a list of boolean checks/tests to a variable, and return (True)
-- if /all/ of them passed.
--
-- /Note/: See 'All' in "Data.Monoid" and 'all' in "Prelude" for reference.
--
-- /See/: <http://www.haskell.org/pipermail/haskell-cafe/2007-February/022694.html>
allCond :: a -> [a -> Bool] -> Bool
allCond _ [] = False
allCond value lst = and . mapV value $ lst
-- | Apply a list of boolean checks/tests to a variable, and return (True)
-- if /any/ of them passed.
--
-- /Note/: See 'Any' in "Data.Monoid" and 'any' in "Prelude" for reference.
--
-- /See/: <http://www.haskell.org/pipermail/haskell-cafe/2007-February/022694.html>
{-# INLINE anyCond #-}
anyCond :: a -> [a -> Bool] -> Bool
anyCond _ [] = False
anyCond value lst = or . mapV value $ lst