packages feed

universum 1.0.4.1 → 1.1.0

raw patch · 12 files changed

+423/−231 lines, 12 filesdep +hedgehogdep +tastydep +tasty-hedgehogdep ~Globdep ~basedep ~bytestring

Dependencies added: hedgehog, tasty, tasty-hedgehog

Dependency ranges changed: Glob, base, bytestring, containers, criterion, deepseq, doctest, microlens, microlens-mtl

Files

CHANGES.md view
@@ -1,3 +1,23 @@+1.1.0+=====++* [#144](https://github.com/serokell/universum/issues/144):+  Add `Exc` pattern synonym.+* [#60](https://github.com/serokell/universum/issues/60):+  Reexport `Natural` type from `Numeric.Natura` module.+* [#118](https://github.com/serokell/universum/issues/118):+  Reexport `Type` from `Data.Kind` module.+* [#130](https://github.com/serokell/universum/issues/130):+  Merge `ToList` and `Container` type classes into single type class `Container`.+* [#15](https://github.com/serokell/universum/issues/15):+  Add `?:` function to `Universum.Monad.Maybe`.+* [#128](https://github.com/serokell/universum/issues/128):+  Add `Unsafe` module with unsafe functions to works with lists and `Maybe`.+* [#129](https://github.com/serokell/universum/issues/129):+  Reexport `id`.+* [#136](https://github.com/serokell/universum/issues/136):+  Change `foldl'` type back, add `flipfoldl'` instead.+ 1.0.4.1 ===== @@ -63,6 +83,8 @@   Remove `NontrivialContainer` constraint alias. * [#56](https://github.com/serokell/universum/issues/56):   Make `elem` and `notElem` faster for `Set` and `HashSet` by introducing `ElementConstraint` associated type family.+* Remove `Unsafe` module. Though, see issue [#128](https://github.com/serokell/universum/issues/128)+  for disuccion regarding possible return of this module.  0.9.1 =====
README.md view
@@ -59,6 +59,8 @@  1. Avoid all [partial functions](https://www.reddit.com/r/haskell/comments/5n51u3/why_are_partial_functions_as_in_head_tail_bad/).    We like [total](http://mathworld.wolfram.com/TotalFunction.html) and exception-free functions.+   Though you can still use some unsafe functions from `Universum.Unsafe` module+   but they are not exported by default. 2. Use more efficient [string representations](https://www.reddit.com/r/haskell/comments/29jw0s/whats_wrong_with_string/).    `String` type is crushingly inefficient. All our functions either try to be polymorphic over string    types, or use [`Text`](http://hackage.haskell.org/package/text-1.2.2.1/docs/Data-Text.html)@@ -116,17 +118,18 @@ Gotchas ------- -* `id` is renamed to `identity` (because it's nice to be able to use `id` as a variable name). * `head`, `tail`, `last`, `init` work with `NonEmpty a` instead of `[a]`. * Safe analogue for `head` function: `safeHead :: [a] -> Maybe a`.-* `undefined` triggers a compiler warning, which is probably not what you want. Either use `throwIO`, `Except`, or `error`.+* `undefined` triggers a compiler warning, which is probably not what you want. Either use `throwIO`, `Except`, `error` or `bug`. * `map` is `fmap` now.-* `sortOn` is available without import. This function efficiently sorts a list based on some-  property of its elements (e.g. `sortOn length` would sort elements by length).+* Multiple sorting functions are available without imports:+  + `sortBy :: (a -> a -> Ordering) -> [a] -> [a]`: sorts list using given custom comparator.+  + `sortWith :: Ord b => (a -> b) -> [a] -> [a]`: sorts a list based on some property of its elements.+  + `sortOn :: Ord b => (a -> b) -> [a] -> [a]`: just like `sortWith`, but more time-efficient if function is calculated slowly (though less space-efficient). So you should write `sortOn length` (would sort elements by length) but `sortWith fst` (would sort list of pairs by first element). * Functions `sum` and `product` are strict now, which makes them more efficient. * If you try to do something like `putStrLn "hi"`, you'll get an error message if   `OverloadedStrings` is enabled – it happens because the compiler doesn't know what-  type to infer for the string. Use `putText` in this case.+  type to infer for the string. Use `putTextLn` in this case. * Since `show` doesn't come from `Show` anymore, you can't write `Show` instances easily.   Either use autoderived instances or   [`Buildable`](https://github.com/serokell/universum/blob/f2ccf8afd862e37ccd204c0ef9efde48a05c2d29/src/Universum.hs#L144).@@ -143,7 +146,6 @@ * As a consequence of previous point, some functions like `traverse_`, `forM_`, `sequenceA_`, etc.   are generalized over `Container` type classes. * `error` takes `Text`.-* `foldl'` takes a function with its arguments flipped compared with the `base` version.   Things that you were already using, but now you don't have to import them explicitly@@ -158,7 +160,7 @@ Then, some commonly used types: `Map/HashMap/IntMap`, `Set/HashSet/IntSet`, `Seq`, `Text` and `ByteString` (as well as synonyms `LText` and `LByteString` for lazy versions). -`liftIO` and `MonadIO` are exported by default. A lot of functions are generalized to `MonadIO`.+`liftIO` and `MonadIO` are exported by default. A lot of `IO` functions are generalized to `MonadIO`.  `deepseq` is exported. For instance, if you want to force deep evaluation of some value (in IO), you can write `evaluateNF a`. WHNF evaluation is possible with `evaluateWHNF a`.@@ -176,7 +178,7 @@ We export `Text` and `LText`, and some functions work with `Text` instead of `String` – specifically, IO functions (`readFile`, `putStrLn`, etc) and `show`. In fact, `show` is polymorphic and can produce strict or lazy `Text`, `String`, or `ByteString`.-Also, `toS` can convert any string type to any string type, but you can use `toText`, `toByteString`, `toString` functions to convert to any specific type.+Also, `toText/toLText/toString` can convert `Text|LText|String` types to `Text/LText/String`. If you want to convert to and from `ByteString` use `encodeUtf8/decodeUtf8` functions.  ### Debugging and `undefined`s @@ -201,7 +203,7 @@ * `ordNub` and `sortNub` are _O(n log n)_ versions of `nub` (which is quadratic)   and `hashNub` and `unstableNub` are almost _O(n)_ versions of `nub`. * `(&)` – reverse application. `x & f & g` instead of `g $ f $ x` is useful sometimes.-* `pretty` and `prettyL` for converting `Buildable` into `Text` (can be used instead of `show`).+* `pretty` and `prettyL` for converting `Buildable` into `Text` (suggested be used instead of `show`). * `whenM`, `unlessM`, `ifM`, `guardM` are available and do what you expect   them to do (e.g. `whenM (doesFileExist "foo")`). * Very generalized version of `concatMapM`, too, is available and does what expected.@@ -277,7 +279,16 @@   for creating singleton containers. Even monomorhpic ones like `Text`. * `evaluateWHNF` and `evaluateNF` functions as clearer and lifted aliases for   `evaluate` and `evaluate . force`.-* `ToPairs` type class for data types that can be converted to list of pairs.+* `ToPairs` type class for data types that can be converted to list of pairs (like `Map` or `HashMap` or `IntMap`).++Projects that use Universum+---------------------------++- [cardano-report-server](https://github.com/input-output-hk/cardano-report-server)+- [cardano-sl](https://github.com/input-output-hk/cardano-sl)+- [importify](https://github.com/serokell/importify)+- [log-warper](https://github.com/serokell/log-warper)+- [orgstat](https://github.com/volhovm/orgstat)  License -------
benchmark/Main.hs view
@@ -10,17 +10,17 @@ import Data.List (group, head, nub, sort, zip5) import Data.Text (Text) +import Universum.Container (flipfoldl', foldl') import Universum.Monad (concatMapM) import Universum.Nub (hashNub, ordNub, sortNub, unstableNub) import Universum.VarArg ((...))  import qualified Data.Foldable as Foldable (elem) import qualified Data.HashSet as HashSet (fromList, insert)-import qualified Data.List as List (foldl') import qualified Data.List.NonEmpty as NonEmpty import qualified Data.Set as Set (fromList) import qualified Data.Text as T-import qualified Universum.Container as Container (elem, foldl')+import qualified Universum.Container as Container (elem)  main :: IO () main = defaultMain@@ -173,10 +173,9 @@ -- | Checks that 'foldl'' is implemented efficiently for 'Universum.List' bgroupFold :: Benchmark bgroupFold = do-    let testList        = [1..100000] :: [Int]-    let universumFoldl' = Container.foldl' HashSet.insert mempty-    let ghcFoldl'       = List.foldl' (\hashSet element -> -                          HashSet.insert element hashSet) mempty-    bgroup "foldl'" [ bench "universum" $ nf universumFoldl' testList-                    , bench "base"      $ nf ghcFoldl' testList+    let testList   = [1..100000] :: [Int]+    let flipFoldl' = flipfoldl' HashSet.insert mempty+    let ghcFoldl'  = foldl' (\hashSet element -> HashSet.insert element hashSet) mempty+    bgroup "foldl'" [ bench "flipped" $ nf flipFoldl' testList+                    , bench "base"    $ nf ghcFoldl'  testList                     ]
src/Universum/Base.hs view
@@ -11,6 +11,7 @@        , module Data.Char        , module Data.Int        , module Data.Word+       , Natural           -- * Base type classes        , module Data.Eq@@ -22,11 +23,10 @@        , module System.IO           -- * Base GHC types-#if ( __GLASGOW_HASKELL__ >= 710 )        , module Data.Proxy        , module Data.Typeable        , module Data.Void-#endif+        , module GHC.Base        , module GHC.Enum        , module GHC.Exts@@ -47,6 +47,9 @@        , module GHC.OverloadedLabels        , module GHC.ExecutionStack        , module GHC.Stack++         -- * Data.Kind+       , Type #endif         , ($!)@@ -57,6 +60,7 @@ import Data.Char (chr) import Data.Int (Int, Int16, Int32, Int64, Int8) import Data.Word (Word, Word16, Word32, Word64, Word8, byteSwap16, byteSwap32, byteSwap64)+import Numeric.Natural (Natural)  -- IO import System.IO (FilePath, Handle, IOMode (..), stderr, stdin, stdout, withFile)@@ -98,24 +102,13 @@                   prettyCallStack, prettySrcLoc, withFrozenCallStack) #endif +#if ( __GLASGOW_HASKELL__ >= 800 )+-- TODO: move Constraint here later+import Data.Kind (Type)+#endif+ -- $setup -- >>> import Universum.Function (const, ($))---- Pending GHC 8.2 we'll expose these.--{--import GHC.Records as X (-    HasField(..)-  )--<<<<<<< HEAD-=======-import Data.Kind as X (-    type (*)-  , type Type-  )--}-  -- | Stricter version of 'Data.Function.$' operator. -- Default Prelude defines this at the toplevel module, so we do as well.
src/Universum/Container/Class.hs view
@@ -21,10 +21,11 @@  module Universum.Container.Class        ( -- * Foldable-like classes and methods-         ToList    (..)-       , ToPairs   (..)+         ToPairs   (..)        , Container (..) +       , flipfoldl'+        , sum        , product @@ -64,8 +65,6 @@  import qualified Data.Foldable as Foldable -import qualified Data.List as List (null)- import qualified Data.Sequence as SEQ  import qualified Data.ByteString as BS@@ -91,124 +90,6 @@ -- >>> import qualified Data.HashMap.Strict as HashMap  ------------------------------------------------------------------------------- Containers (e.g. tuples aren't containers)--------------------------------------------------------------------------------- | Default implementation of 'Element' associated type family.-type family ElementDefault (t :: *) :: * where-    ElementDefault (f a) = a---- | Type class for data types that can be converted to List.--- Contains very small and safe subset of 'Foldable' functions.------ You can define 'Tolist' by just defining 'toList' function.--- But the following law should be met:------ @'null' ≡ 'List.null' . 'toList'@----class ToList t where-    -- | Type of element for some container. Implemented as an asscociated type family because-    -- some containers are monomorphic over element type (like 'T.Text', 'IntSet', etc.)-    -- so we can't implement nice interface using old higher-kinded types-    -- approach. Implementing this as an associated type family instead of-    -- top-level family gives you more control over element types.-    type Element t :: *-    type Element t = ElementDefault t--    -- | Convert container to list of elements.-    ---    -- >>> toList (Just True)-    -- [True]-    -- >>> toList @Text "aba"-    -- "aba"-    -- >>> :t toList @Text "aba"-    -- toList @Text "aba" :: [Char]-    toList :: t -> [Element t]-    default toList :: (Foldable f, t ~ f a, Element t ~ a) => t -> [Element t]-    toList = Foldable.toList-    {-# INLINE toList #-}--    -- | Checks whether container is empty.-    ---    -- >>> null @Text ""-    -- True-    -- >>> null @Text "aba"-    -- False-    null :: t -> Bool-    null = List.null . toList-    {-# INLINE null #-}--------------------------------------------------------------------------------- Instances for monomorphic containers-------------------------------------------------------------------------------instance ToList T.Text where-    type Element T.Text = Char-    toList = T.unpack-    {-# INLINE toList #-}-    null = T.null-    {-# INLINE null #-}--instance ToList TL.Text where-    type Element TL.Text = Char-    toList = TL.unpack-    {-# INLINE toList #-}-    null = TL.null-    {-# INLINE null #-}--instance ToList BS.ByteString where-    type Element BS.ByteString = Word8-    toList = BS.unpack-    {-# INLINE toList #-}-    null = BS.null-    {-# INLINE null #-}--instance ToList BSL.ByteString where-    type Element BSL.ByteString = Word8-    toList = BSL.unpack-    {-# INLINE toList #-}-    null = BSL.null-    {-# INLINE null #-}--instance ToList IntSet where-    type Element IntSet = Int-    toList = IS.toList-    {-# INLINE toList #-}-    null = IS.null-    {-# INLINE null #-}--------------------------------------------------------------------------------- Boilerplate instances (duplicate Foldable)--------------------------------------------------------------------------------- Basic types-instance ToList [a]-instance ToList (Maybe a)-instance ToList (Either a b)-instance ToList (Identity a)-instance ToList (Const a b)--#if __GLASGOW_HASKELL__ >= 800--- Algebraic types-instance ToList (Dual a)-instance ToList (First a)-instance ToList (Last a)-instance ToList (Product a)-instance ToList (Sum a)-instance ToList (NonEmpty a)-instance ToList (ZipList a)-#endif---- Containers-instance ToList (HashMap k v)-instance ToList (HashSet v)-instance ToList (IntMap v)-instance ToList (Map k v)-instance ToList (Set v)-instance ToList (Seq a)-instance ToList (Vector a)------------------------------------------------------------------------------ -- ToPairs ---------------------------------------------------------------------------- @@ -284,19 +165,63 @@     elems   = M.elems     {-# INLINE elems #-} - ------------------------------------------------------------------------------- Additional operations that don't make much sense for e.g. Maybe+-- Containers (e.g. tuples and Maybe aren't containers) ---------------------------------------------------------------------------- --- | A class for 'ToList's that aren't trivial like 'Maybe' (e.g. can hold--- more than one value)-class ToList t => Container t where+-- | Default implementation of 'Element' associated type family.+type family ElementDefault (t :: *) :: * where+    ElementDefault (f a) = a++-- | Very similar to 'Foldable' but also allows instances for monomorphic types+-- like 'Text' but forbids instances for 'Maybe' and similar. This class is used as+-- a replacement for 'Foldable' type class. It solves the following problems:+--+-- 1. 'length', 'foldr' and other functions work on more types for which it makes sense.+-- 2. You can't accidentally use 'length' on polymorphic 'Foldable' (like list),+--    replace list with 'Maybe' and then debug error for two days.+-- 3. More efficient implementaions of functions for polymorphic types (like 'elem' for 'Set').+--+-- The drawbacks:+--+-- 1. Type signatures of polymorphic functions look more scary.+-- 2. Orphan instances are involved if you want to use 'foldr' (and similar) on types from libraries.+class Container t where+    -- | Type of element for some container. Implemented as an asscociated type family because+    -- some containers are monomorphic over element type (like 'T.Text', 'IntSet', etc.)+    -- so we can't implement nice interface using old higher-kinded types+    -- approach. Implementing this as an associated type family instead of+    -- top-level family gives you more control over element types.+    type Element t :: *+    type Element t = ElementDefault t+     -- | Constraint for elements. This can be used to implement more efficient-    -- implementation of some methods.+    -- implementation of some methods. For example 'elem' for 'Set' and 'HashSet'.     type ElementConstraint t :: * -> Constraint     type ElementConstraint t = Eq +    -- | Convert container to list of elements.+    --+    -- >>> toList @Text "aba"+    -- "aba"+    -- >>> :t toList @Text "aba"+    -- toList @Text "aba" :: [Char]+    toList :: t -> [Element t]+    default toList :: (Foldable f, t ~ f a, Element t ~ a) => t -> [Element t]+    toList = Foldable.toList+    {-# INLINE toList #-}++    -- | Checks whether container is empty.+    --+    -- >>> null @Text ""+    -- True+    -- >>> null @Text "aba"+    -- False+    null :: t -> Bool+    default null :: (Foldable f, t ~ f a, Element t ~ a) => t -> Bool+    null = Foldable.null+    {-# INLINE null #-}+     foldr :: (Element t -> b -> b) -> b -> t -> b     default foldr :: (Foldable f, t ~ f a, Element t ~ a) => (Element t -> b -> b) -> b -> t -> b     foldr = Foldable.foldr@@ -307,9 +232,9 @@     foldl = Foldable.foldl     {-# INLINE foldl #-} -    foldl' :: (Element t -> b -> b) -> b -> t -> b-    default foldl' :: (Foldable f, t ~ f a, Element t ~ a) => (Element t -> b -> b) -> b -> t -> b-    foldl' f = Foldable.foldl' (flip f)+    foldl' :: (b -> Element t -> b) -> b -> t -> b+    default foldl' :: (Foldable f, t ~ f a, Element t ~ a) => (b -> Element t -> b) -> b -> t -> b+    foldl' = Foldable.foldl'     {-# INLINE foldl' #-}      length :: t -> Int@@ -411,11 +336,16 @@ ----------------------------------------------------------------------------  instance Container T.Text where+    type Element T.Text = Char+    toList = T.unpack+    {-# INLINE toList #-}+    null = T.null+    {-# INLINE null #-}     foldr = T.foldr     {-# INLINE foldr #-}     foldl = T.foldl     {-# INLINE foldl #-}-    foldl' f = T.foldl' (flip f)+    foldl' = T.foldl'     {-# INLINE foldl' #-}     foldr1 = T.foldr1     {-# INLINE foldr1 #-}@@ -439,11 +369,16 @@     {-# INLINE safeHead #-}  instance Container TL.Text where+    type Element TL.Text = Char+    toList = TL.unpack+    {-# INLINE toList #-}+    null = TL.null+    {-# INLINE null #-}     foldr = TL.foldr     {-# INLINE foldr #-}     foldl = TL.foldl     {-# INLINE foldl #-}-    foldl' f = TL.foldl' (flip f)+    foldl' = TL.foldl'     {-# INLINE foldl' #-}     foldr1 = TL.foldr1     {-# INLINE foldr1 #-}@@ -468,11 +403,16 @@     {-# INLINE safeHead #-}  instance Container BS.ByteString where+    type Element BS.ByteString = Word8+    toList = BS.unpack+    {-# INLINE toList #-}+    null = BS.null+    {-# INLINE null #-}     foldr = BS.foldr     {-# INLINE foldr #-}     foldl = BS.foldl     {-# INLINE foldl #-}-    foldl' f = BS.foldl' (flip f)+    foldl' = BS.foldl'     {-# INLINE foldl' #-}     foldr1 = BS.foldr1     {-# INLINE foldr1 #-}@@ -498,11 +438,16 @@     {-# INLINE safeHead #-}  instance Container BSL.ByteString where+    type Element BSL.ByteString = Word8+    toList = BSL.unpack+    {-# INLINE toList #-}+    null = BSL.null+    {-# INLINE null #-}     foldr = BSL.foldr     {-# INLINE foldr #-}     foldl = BSL.foldl     {-# INLINE foldl #-}-    foldl' f = BSL.foldl' (flip f)+    foldl' = BSL.foldl'     {-# INLINE foldl' #-}     foldr1 = BSL.foldr1     {-# INLINE foldr1 #-}@@ -528,11 +473,16 @@     {-# INLINE safeHead #-}  instance Container IntSet where+    type Element IntSet = Int+    toList = IS.toList+    {-# INLINE toList #-}+    null = IS.null+    {-# INLINE null #-}     foldr = IS.foldr     {-# INLINE foldr #-}     foldl = IS.foldl     {-# INLINE foldl #-}-    foldl' f = IS.foldl' (flip f)+    foldl' = IS.foldl'     {-# INLINE foldl' #-}     length = IS.size     {-# INLINE length #-}@@ -596,6 +546,16 @@  -- TODO: I should put different strings for different versions but I'm too lazy to do it... +{- | Similar to 'foldl'' but takes a function with its arguments flipped.++>>> flipfoldl' (/) 5 [2,3] :: Rational+15 % 2++-}+flipfoldl' :: (Container t, Element t ~ a) => (a -> b -> b) -> b -> t -> b+flipfoldl' f = foldl' (flip f)+{-# INLINE flipfoldl' #-}+ #if MIN_VERSION_base(4,10,1) -- | Stricter version of 'Prelude.sum'. --@@ -710,51 +670,17 @@         :$$: Text "" #endif -#define DISALLOW_TO_LIST_8(t, z) \-    instance TypeError (DisallowInstance z) => \-      ToList (t) where { \-        toList = undefined; \-        null = undefined; } \--#define DISALLOW_CONTAINER_8(t, z) \-    instance TypeError (DisallowInstance z) => \-      Container (t) where { \-        foldr = undefined; \-        foldl = undefined; \-        foldl' = undefined; \-        length = undefined; \-        elem = undefined; \-        maximum = undefined; \-        minimum = undefined; } \--#define DISALLOW_TO_LIST_7(t) \-    instance ForbiddenFoldable (t) => ToList (t) where { \-        toList = undefined; \-        null = undefined; } \--#define DISALLOW_CONTAINER_7(t) \-    instance ForbiddenFoldable (t) => Container (t) where { \-        foldr = undefined; \-        foldl = undefined; \-        foldl' = undefined; \-        length = undefined; \-        elem = undefined; \-        maximum = undefined; \-        minimum = undefined; } \- #if __GLASGOW_HASKELL__ >= 800-DISALLOW_TO_LIST_8((a, b),"tuples")-DISALLOW_CONTAINER_8((a, b),"tuples")-DISALLOW_CONTAINER_8(Maybe a,"Maybe")-DISALLOW_CONTAINER_8(Identity a,"Identity")-DISALLOW_CONTAINER_8(Either a b,"Either")+instance TypeError (DisallowInstance "tuple")    => Container (a, b)+instance TypeError (DisallowInstance "Maybe")    => Container (Maybe a)+instance TypeError (DisallowInstance "Either")   => Container (Either a b)+instance TypeError (DisallowInstance "Identity") => Container (Identity a) #else class ForbiddenFoldable a-DISALLOW_TO_LIST_7((a, b))-DISALLOW_CONTAINER_7((a, b))-DISALLOW_CONTAINER_7(Maybe a)-DISALLOW_CONTAINER_7(Identity a)-DISALLOW_CONTAINER_7(Either a b)+instance ForbiddenFoldable (a, b)       => Container (a, b)+instance ForbiddenFoldable (Maybe a)    => Container (Maybe a)+instance ForbiddenFoldable (Either a b) => Container (Either a b)+instance ForbiddenFoldable (Identity a) => Container (Identity a) #endif  ----------------------------------------------------------------------------
src/Universum/Exception.hs view
@@ -1,6 +1,8 @@ {-# LANGUAGE CPP                   #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PatternSynonyms       #-} {-# LANGUAGE Safe                  #-}+{-# LANGUAGE ViewPatterns          #-}  -- | Re-exports most useful functionality from 'safe-exceptions'. Also -- provides some functions to work with exceptions over 'MonadError'.@@ -10,6 +12,7 @@ #if ( __GLASGOW_HASKELL__ >= 800 )        , Bug (..)        , bug+       , pattern Exc #endif        , note        ) where@@ -20,9 +23,9 @@                                catchAny, displayException, finally, handleAny, mask_, onException,                                throwM, try, tryAny) -import Control.Applicative (Applicative (pure)) import Control.Monad.Except (MonadError, throwError)-import Data.Maybe (Maybe, maybe)+import Universum.Applicative (Applicative (pure))+import Universum.Monad (Maybe (..), maybe)  #if ( __GLASGOW_HASKELL__ >= 800 ) import Data.List ((++))@@ -55,4 +58,33 @@ #else note :: (MonadError e m, Applicative m) => e -> Maybe a -> m a note err = maybe (throwError err) pure+#endif+++#if ( __GLASGOW_HASKELL__ >= 800 )+{- | Pattern synonym to easy pattern matching on exceptions. So intead of+writing something like this:++@+isNonCriticalExc e+    | Just (_ :: NodeAttackedError) <- fromException e = True+    | Just DialogUnexpected{} <- fromException e = True+    | otherwise = False+@++you can use 'Exc' pattern synonym:++@+isNonCriticalExc = \case+    Exc (_ :: NodeAttackedError) -> True  -- matching all exceptions of type 'NodeAttackedError'+    Exc DialogUnexpected{} -> True+    _ -> False+@++This pattern is bidirectional. You can use @Exc e@ instead of @toException e@.+-}+pattern Exc :: Exception e => e -> SomeException+pattern Exc e <- (fromException -> Just e)+  where+    Exc e = toException e #endif
src/Universum/Function.hs view
@@ -3,8 +3,9 @@        , identity        ) where -import Data.Function (const, fix, flip, on, ($), (.))+import Data.Function (const, fix, flip, id, on, ($), (.))  -- | Renamed version of 'Prelude.id'. identity :: a -> a-identity x = x+identity = id+{-# INLINE identity #-}
src/Universum/Monad/Maybe.hs view
@@ -3,7 +3,8 @@ -- | Utility functions to work with 'Data.Maybe' data type as monad.  module Universum.Monad.Maybe-       ( whenJust+       ( (?:)+       , whenJust        , whenJustM        , whenNothing        , whenNothing_@@ -12,12 +13,28 @@        ) where  import Universum.Applicative (Applicative, pass, pure)+import Universum.Monad.Reexport (fromMaybe) import Universum.Monad.Reexport (Maybe (..), Monad (..))  -- $setup -- >>> import Universum.Bool (Bool (..)) -- >>> import Universum.Function (($)) -- >>> import Universum.Print (putTextLn)+-- >>> import Universum.String (readMaybe)++{- | Similar to 'fromMaybe' but with flipped arguments.++>>> readMaybe "True" ?: False+True++>>> readMaybe "Tru" ?: False+False++-}+infixr 0 ?:+(?:) :: Maybe a -> a -> a+mA ?: b = fromMaybe b mA+{-# INLINE (?:) #-}  -- | Specialized version of 'for_' for 'Maybe'. It's used for code readability. -- Also helps to avoid space leaks:
+ src/Universum/Unsafe.hs view
@@ -0,0 +1,35 @@+{-# LANGUAGE Unsafe #-}++{- | Unsafe functions to work with lists and 'Maybe'.+Sometimes unavoidable but better don't use them. This module+is intended to be imported qualified and it's not even included+in default prelude exports.++@+import qualified Universum.Unsafe as Unsafe++foo :: [a] -> a+foo = Unsafe.head+@++-}++module Universum.Unsafe+       ( head+       , tail+       , init+       , last+       , at+       , (!!)+       , fromJust+       ) where++import Data.List (head, init, last, tail, (!!))+import Data.Maybe (fromJust)++import Universum.Base (Int)+import Universum.Function (flip)++-- | Similar to '!!' but with flipped arguments.+at :: Int -> [a] -> a+at = flip (!!)
+ test/Spec.hs view
@@ -0,0 +1,9 @@+module Main where++import Test.Tasty (defaultMain)++import Test.Universum.Property (hedgehogTestTree)++main :: IO ()+main = do+    defaultMain hedgehogTestTree
+ test/Test/Universum/Property.hs view
@@ -0,0 +1,129 @@+module Test.Universum.Property+        ( hedgehogTestTree+        ) where++import Universum ++import Data.List (nub)+import Hedgehog (Property, Gen, MonadGen, forAll, property, assert, (===))+import Test.Tasty (testGroup, TestTree)+import Test.Tasty.Hedgehog++import qualified Universum as U+import qualified Hedgehog.Gen              as Gen+import qualified Hedgehog.Range            as Range+import qualified Data.ByteString           as B+import qualified Data.ByteString.Lazy      as LB+import qualified Data.Text                 as T+import qualified Data.Text.Lazy            as LT++++hedgehogTestTree :: TestTree+hedgehogTestTree = testGroup "Tests" [utfProps, listProps, boolMProps]++utfProps :: TestTree+utfProps = testGroup "utf8 conversion property tests" +    [ testProperty "String to ByteString invertible" prop_StringToBytes+    , testProperty "Text to ByteString invertible" prop_TextToBytes+    , testProperty "ByteString to Text or String invertible" prop_BytesTo+    ]++unicode' :: MonadGen m => m U.Char+unicode' = do+    a <- Gen.unicode+    if U.elem a ['\65534', '\65535']+    then unicode'+    else return a++utf8String :: Gen U.String+utf8String = Gen.string (Range.linear 0 10000) unicode'++utf8Text :: Gen T.Text+utf8Text = Gen.text (Range.linear 0 10000) unicode'++utf8Bytes :: Gen B.ByteString+utf8Bytes = Gen.utf8 (Range.linear 0 10000) unicode'++-- "\65534" fails, but this is from BU.toString+-- > import qualified Data.ByteString.UTF8 as BU+-- > BU.toString (BU.fromString "\65534") == "\65533"+-- > True ++prop_StringToBytes :: Property+prop_StringToBytes = property $ do+    str <- forAll utf8String+    assert $ str == (decodeUtf8 (encodeUtf8 str :: B.ByteString))+          && str == (decodeUtf8 (encodeUtf8 str :: LB.ByteString))+++prop_TextToBytes :: Property+prop_TextToBytes = property $ do+    txt <- forAll utf8Text+    assert $ txt == (decodeUtf8 (encodeUtf8 txt :: B.ByteString))+          && txt == (decodeUtf8 (encodeUtf8 txt :: LB.ByteString))++-- "\239\191\190" fails, but this is the same as "\65534" :: String+prop_BytesTo :: Property+prop_BytesTo = property $ do+    utf <- forAll utf8Bytes+    assert $ utf == (encodeUtf8 (decodeUtf8 utf :: U.String))+          && utf == (encodeUtf8 (decodeUtf8 utf :: T.Text))+          && utf == (encodeUtf8 (decodeUtf8 utf :: LT.Text))++-- ordNub++listProps :: TestTree+listProps = testGroup "list function property tests" +    [ testProperty "Hedgehog ordNub xs == nub xs" prop_ordNubCorrect+    , testProperty "Hedgehog hashNub xs == nub xs" prop_hashNubCorrect+    , testProperty "Hedgehog sortNub xs == sort $ nub xs" prop_sortNubCorrect+    , testProperty "Hedgehog sort $ unstableNub xs == sort $ nub xs" prop_unstableNubCorrect+    ]++genIntList :: Gen [U.Int]+genIntList = Gen.list (Range.linear 0 10000) Gen.enumBounded++prop_ordNubCorrect :: Property+prop_ordNubCorrect = property $ do+    xs <- forAll genIntList+    U.ordNub xs === nub xs++prop_hashNubCorrect :: Property+prop_hashNubCorrect = property $ do+    xs <- forAll genIntList+    U.hashNub xs === nub xs++prop_sortNubCorrect :: Property+prop_sortNubCorrect = property $ do+    xs <- forAll genIntList+    U.sortNub xs === (U.sort $ nub xs)++prop_unstableNubCorrect :: Property+prop_unstableNubCorrect = property $ do+    xs <- forAll genIntList+    (U.sort $ U.unstableNub xs) === (U.sort $ nub xs)+++-- logicM+-- this section needs a little more thought++genBoolList :: Gen [U.Bool]+genBoolList = Gen.list (Range.linear 0 1000) Gen.bool++boolMProps :: TestTree+boolMProps = testGroup "lifted logic function property tests"+    [ testProperty "Hedgehog andM" prop_andM+    , testProperty "Hedgehog orM" prop_orM+    ]++prop_andM :: Property+prop_andM = property $ do+    bs <- forAll genBoolList+    U.andM (return <$> bs) === ((return $ U.and bs) :: U.Maybe U.Bool)++prop_orM :: Property+prop_orM = property $ do+    bs <- forAll genBoolList+    U.orM (return <$> bs) === ((return $ U.or bs) :: U.Maybe U.Bool)+
universum.cabal view
@@ -1,5 +1,5 @@ name: universum-version: 1.0.4.1+version: 1.1.0 cabal-version: >=1.18 build-type: Simple license: MIT@@ -62,16 +62,17 @@         Universum.String.Conversion         Universum.String.Reexport         Universum.TypeOps+        Universum.Unsafe         Universum.VarArg     build-depends:         base >=4.8 && <5,-        bytestring >=0.10.8.2,-        containers >=0.5.10.2,-        deepseq >=1.4.3.0,+        bytestring >=0.10.8.1,+        containers >=0.5.7.1,+        deepseq >=1.4.2.0,         ghc-prim >=0.4.0.0,         hashable >=1.2.6.1,-        microlens >=0.4.8.3,-        microlens-mtl >=0.1.11.1,+        microlens >=0.4.8.1,+        microlens-mtl >=0.1.11.0,         mtl >=2.2.1,         safe-exceptions >=0.1.6.0,         stm >=2.4.4.1,@@ -87,13 +88,30 @@     hs-source-dirs: src     ghc-options: -Wall -fwarn-implicit-prelude +test-suite  universum-test+    type: exitcode-stdio-1.0+    main-is: Spec.hs+    build-depends:+        base >=4.8 && <5,+        universum -any,+        bytestring >=0.10.8.1,+        text >=1.2.2.2,+        utf8-string >=1.0.1.1,+        hedgehog >=0.5.1,+        tasty >=0.11.3,+        tasty-hedgehog >=0.1.0.2+    default-language: Haskell2010+    hs-source-dirs: test+    other-modules:+        Test.Universum.Property+    ghc-options: -Wall -threaded test-suite  universum-doctest     type: exitcode-stdio-1.0     main-is: Doctest.hs     build-depends:         base >=4.8 && <5,-        doctest >=0.13.0,-        Glob >=0.9.1+        doctest >=0.11.4,+        Glob >=0.8.0     default-language: Haskell2010     hs-source-dirs: test     ghc-options: -threaded@@ -102,11 +120,11 @@     type: exitcode-stdio-1.0     main-is: Main.hs     build-depends:-        base >=4.10.1.0 && <5,+        base >=4.9.1.0 && <5,         universum -any,-        containers >=0.5.10.2,-        criterion >=1.2.6.0,-        deepseq >=1.4.3.0,+        containers >=0.5.7.1,+        criterion >=1.1.4.0,+        deepseq >=1.4.2.0,         hashable >=1.2.6.1,         mtl >=2.2.1,         semigroups >=0.18.3,