mirror of
https://github.com/kowainik/relude.git
synced 2024-09-11 04:35:39 +03:00
[#53] Add doctest integration
This commit is contained in:
parent
f0f0621abb
commit
3a6b66b22d
@ -1,3 +1,9 @@
|
||||
1.0.4
|
||||
=====
|
||||
|
||||
* [#53](https://github.com/serokell/universum/issues/53):
|
||||
Add `doctest` to `universum`. Also imporove and fix documentation.
|
||||
|
||||
1.0.3
|
||||
=====
|
||||
|
||||
|
@ -12,6 +12,9 @@ module Universum.Applicative
|
||||
import Control.Applicative (Alternative (..), Applicative (..), Const (..), ZipList (..), liftA2,
|
||||
liftA3, optional, (<**>))
|
||||
|
||||
-- $setup
|
||||
-- >>> import Universum.Monad (Maybe)
|
||||
|
||||
-- | Shorter alias for @pure ()@.
|
||||
--
|
||||
-- >>> pass :: Maybe ()
|
||||
|
@ -98,6 +98,9 @@ import GHC.Stack (CallStack, HasCallStack, callStack, currentCallStack, getCallS
|
||||
prettyCallStack, prettySrcLoc, withFrozenCallStack)
|
||||
#endif
|
||||
|
||||
-- $setup
|
||||
-- >>> import Universum.Function (const, ($))
|
||||
|
||||
-- Pending GHC 8.2 we'll expose these.
|
||||
|
||||
{-
|
||||
@ -117,11 +120,13 @@ import Data.Kind as X (
|
||||
-- | Stricter version of 'Data.Function.$' operator.
|
||||
-- Default Prelude defines this at the toplevel module, so we do as well.
|
||||
--
|
||||
-- >>> const 3 $ undefined
|
||||
-- >>> const 3 $ Prelude.undefined
|
||||
-- 3
|
||||
-- >>> const 3 $! undefined
|
||||
-- >>> const 3 $! Prelude.undefined
|
||||
-- *** Exception: Prelude.undefined
|
||||
-- CallStack (from HasCallStack):
|
||||
-- error, called at libraries/base/GHC/Err.hs:79:14 in base:GHC.Err
|
||||
-- ...
|
||||
($!) :: (a -> b) -> a -> b
|
||||
f $! x = let !vx = x in f vx
|
||||
infixr 0 $!
|
||||
|
@ -9,10 +9,17 @@ import Universum.Bool.Reexport (Bool, guard, unless, when)
|
||||
import Universum.Function (flip)
|
||||
import Universum.Monad (Monad, MonadPlus, (>>=))
|
||||
|
||||
-- $setup
|
||||
-- >>> import Universum.Applicative (pure)
|
||||
-- >>> import Universum.Bool.Reexport (Bool (..))
|
||||
-- >>> import Universum.Function (($))
|
||||
-- >>> import Universum.Monad (Maybe (..))
|
||||
-- >>> import Universum.Print (putTextLn)
|
||||
|
||||
-- | Monadic version of 'when'.
|
||||
--
|
||||
-- >>> whenM (pure False) $ putText "No text :("
|
||||
-- >>> whenM (pure True) $ putText "Yes text :)"
|
||||
-- >>> whenM (pure False) $ putTextLn "No text :("
|
||||
-- >>> whenM (pure True) $ putTextLn "Yes text :)"
|
||||
-- Yes text :)
|
||||
-- >>> whenM (Just True) (pure ())
|
||||
-- Just ()
|
||||
@ -26,16 +33,16 @@ whenM p m = p >>= flip when m
|
||||
|
||||
-- | Monadic version of 'unless'.
|
||||
--
|
||||
-- >>> unlessM (pure False) $ putText "No text :("
|
||||
-- >>> unlessM (pure False) $ putTextLn "No text :("
|
||||
-- No text :(
|
||||
-- >>> unlessM (pure True) $ putText "Yes text :)"
|
||||
-- >>> unlessM (pure True) $ putTextLn "Yes text :)"
|
||||
unlessM :: Monad m => m Bool -> m () -> m ()
|
||||
unlessM p m = p >>= flip unless m
|
||||
{-# INLINE unlessM #-}
|
||||
|
||||
-- | Monadic version of @if-then-else@.
|
||||
--
|
||||
-- >>> ifM (pure True) (putText "True text") (putText "False text")
|
||||
-- >>> ifM (pure True) (putTextLn "True text") (putTextLn "False text")
|
||||
-- True text
|
||||
ifM :: Monad m => m Bool -> m a -> m a -> m a
|
||||
ifM p x y = p >>= \b -> if b then x else y
|
||||
@ -43,14 +50,15 @@ ifM p x y = p >>= \b -> if b then x else y
|
||||
|
||||
-- | Monadic version of 'guard'. Occasionally useful.
|
||||
-- Here some complex but real-life example:
|
||||
-- @
|
||||
-- findSomePath :: IO (Maybe FilePath)
|
||||
--
|
||||
-- somePath :: MaybeT IO FilePath
|
||||
-- somePath = do
|
||||
-- path <- MaybeT findSomePath
|
||||
-- guardM $ liftIO $ doesDirectoryExist path
|
||||
-- return path
|
||||
-- @
|
||||
-- findSomePath :: IO (Maybe FilePath)
|
||||
--
|
||||
-- somePath :: MaybeT IO FilePath
|
||||
-- somePath = do
|
||||
-- path <- MaybeT findSomePath
|
||||
-- guardM $ liftIO $ doesDirectoryExist path
|
||||
-- return path
|
||||
-- @
|
||||
guardM :: MonadPlus m => m Bool -> m ()
|
||||
guardM f = f >>= guard
|
||||
|
@ -41,8 +41,8 @@ module Universum.Container.Class
|
||||
) where
|
||||
|
||||
import Data.Coerce (Coercible, coerce)
|
||||
import Prelude hiding (all, and, any, elem, foldMap, foldl, foldr, mapM_, notElem, or, product,
|
||||
sequence_, sum)
|
||||
import Prelude hiding (all, and, any, elem, foldMap, foldl, foldr, mapM_, notElem, null, or,
|
||||
product, sequence_, sum)
|
||||
|
||||
import Universum.Applicative (Alternative (..), Const, ZipList, pass)
|
||||
import Universum.Base (Constraint, Word8)
|
||||
@ -86,6 +86,10 @@ import qualified Data.Vector.Primitive as VP
|
||||
import qualified Data.Vector.Storable as VS
|
||||
import qualified Data.Vector.Unboxed as VU
|
||||
|
||||
-- $setup
|
||||
-- >>> import Universum.String (Text)
|
||||
-- >>> import qualified Data.HashMap.Strict as HashMap
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
-- Containers (e.g. tuples aren't containers)
|
||||
----------------------------------------------------------------------------
|
||||
@ -228,9 +232,8 @@ class ToPairs t where
|
||||
type Val t :: *
|
||||
|
||||
-- | Converts the structure to the list of the key-value pairs.
|
||||
-- >>> import qualified Data.HashMap as HashMap
|
||||
-- >>> toPairs (HashMap.fromList [('a', "xxx"), ('b', "yyy")])
|
||||
-- [('a', "xxx"), ('b', "yyy")]
|
||||
-- [('a',"xxx"),('b',"yyy")]
|
||||
toPairs :: t -> [(Key t, Val t)]
|
||||
|
||||
-- | Converts the structure to the list of the keys.
|
||||
@ -244,7 +247,7 @@ class ToPairs t where
|
||||
-- | Converts the structure to the list of the values.
|
||||
--
|
||||
-- >>> elems (HashMap.fromList [('a', "xxx"), ('b', "yyy")])
|
||||
-- ["xxx", "yyy"]
|
||||
-- ["xxx","yyy"]
|
||||
elems :: t -> [Val t]
|
||||
elems = map snd . toPairs
|
||||
{-# INLINE elems #-}
|
||||
@ -591,27 +594,53 @@ instance Container (Vector a)
|
||||
-- Derivative functions
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
-- TODO: I should put different strings for different versions but I'm too lazy to do it...
|
||||
|
||||
#if MIN_VERSION_base(4,10,1)
|
||||
-- | Stricter version of 'Prelude.sum'.
|
||||
--
|
||||
-- >>> sum [1..10]
|
||||
-- 55
|
||||
-- >>> sum (Just 3)
|
||||
-- <interactive>:43:1: error:
|
||||
-- ...
|
||||
-- • Do not use 'Foldable' methods on Maybe
|
||||
-- • In the expression: sum (Just 3)
|
||||
-- In an equation for ‘it’: it = sum (Just 3)
|
||||
-- Suggestions:
|
||||
-- Instead of
|
||||
-- for_ :: (Foldable t, Applicative f) => t a -> (a -> f b) -> f ()
|
||||
-- use
|
||||
-- whenJust :: Applicative f => Maybe a -> (a -> f ()) -> f ()
|
||||
-- whenRight :: Applicative f => Either l r -> (r -> f ()) -> f ()
|
||||
-- ...
|
||||
-- Instead of
|
||||
-- fold :: (Foldable t, Monoid m) => t m -> m
|
||||
-- use
|
||||
-- maybeToMonoid :: Monoid m => Maybe m -> m
|
||||
-- ...
|
||||
#endif
|
||||
sum :: (Container t, Num (Element t)) => t -> Element t
|
||||
sum = foldl' (+) 0
|
||||
|
||||
#if MIN_VERSION_base(4,10,1)
|
||||
-- | Stricter version of 'Prelude.product'.
|
||||
--
|
||||
-- >>> product [1..10]
|
||||
-- 3628800
|
||||
-- >>> product (Right 3)
|
||||
-- <interactive>:45:1: error:
|
||||
-- ...
|
||||
-- • Do not use 'Foldable' methods on Either
|
||||
-- • In the expression: product (Right 3)
|
||||
-- In an equation for ‘it’: it = product (Right 3)
|
||||
-- Suggestions:
|
||||
-- Instead of
|
||||
-- for_ :: (Foldable t, Applicative f) => t a -> (a -> f b) -> f ()
|
||||
-- use
|
||||
-- whenJust :: Applicative f => Maybe a -> (a -> f ()) -> f ()
|
||||
-- whenRight :: Applicative f => Either l r -> (r -> f ()) -> f ()
|
||||
-- ...
|
||||
-- Instead of
|
||||
-- fold :: (Foldable t, Monoid m) => t m -> m
|
||||
-- use
|
||||
-- maybeToMonoid :: Monoid m => Maybe m -> m
|
||||
-- ...
|
||||
#endif
|
||||
product :: (Container t, Num (Element t)) => t -> Element t
|
||||
product = foldl' (*) 1
|
||||
|
||||
|
@ -12,6 +12,10 @@ import Universum.Functor.Reexport (Functor (..))
|
||||
map :: Functor f => (a -> b) -> f a -> f b
|
||||
map = fmap
|
||||
|
||||
-- $setup
|
||||
-- >>> import Universum.Base (negate)
|
||||
-- >>> import Universum.Monad (Maybe (..))
|
||||
|
||||
-- | Alias for @fmap . fmap@. Convenient to work with two nested 'Functor's.
|
||||
--
|
||||
-- >>> negate <<$>> Just [1,2,3]
|
||||
|
@ -19,6 +19,13 @@ import Universum.Monoid (NonEmpty (..))
|
||||
import Universum.Functor (fmap)
|
||||
import Universum.Monad (Maybe (..))
|
||||
|
||||
-- $setup
|
||||
-- >>> import Universum.Applicative (pure)
|
||||
-- >>> import Universum.Base ((==), even)
|
||||
-- >>> import Universum.Bool (Bool (..))
|
||||
-- >>> import Universum.Container (length)
|
||||
-- >>> import Universum.Function (($))
|
||||
|
||||
-- | Returns default list if given list is empty.
|
||||
-- Otherwise applies given function to every element.
|
||||
--
|
||||
|
@ -20,19 +20,26 @@ import Universum.Container (Container, Element, fold, toList)
|
||||
import Universum.Functor (fmap)
|
||||
import Universum.Monad.Reexport (Monad (..))
|
||||
|
||||
-- $setup
|
||||
-- :set -XOverloadedStrings
|
||||
-- >>> import Universum.Base (even)
|
||||
-- >>> import Universum.Monad (Maybe (..), (>=>))
|
||||
-- >>> import Universum.Print (putTextLn)
|
||||
-- >>> import Universum.String (readMaybe)
|
||||
|
||||
-- | Lifting bind into a monad. Generalized version of @concatMap@
|
||||
-- that works with a monadic predicate. Old and simpler specialized to list
|
||||
-- version had next type:
|
||||
--
|
||||
-- @
|
||||
-- concatMapM :: Monad m => (a -> m [b]) -> [a] -> m [b]
|
||||
-- concatMapM :: Monad m => (a -> m [b]) -> [a] -> m [b]
|
||||
-- @
|
||||
--
|
||||
-- Side note: previously it had type
|
||||
--
|
||||
-- @
|
||||
-- concatMapM :: (Applicative q, Monad m, Traversable m)
|
||||
-- => (a -> q (m b)) -> m a -> q (m b)
|
||||
-- concatMapM :: (Applicative q, Monad m, Traversable m)
|
||||
-- => (a -> q (m b)) -> m a -> q (m b)
|
||||
-- @
|
||||
--
|
||||
-- Such signature didn't allow to use this function when traversed container
|
||||
@ -40,7 +47,7 @@ import Universum.Monad.Reexport (Monad (..))
|
||||
-- Now you can use it like e.g.
|
||||
--
|
||||
-- @
|
||||
-- concatMapM readFile files >>= putStrLn
|
||||
-- concatMapM readFile files >>= putTextLn
|
||||
-- @
|
||||
concatMapM
|
||||
:: ( Applicative f
|
||||
@ -76,7 +83,7 @@ concatForM = flip concatMapM
|
||||
-- Just False
|
||||
-- >>> andM [Just True, Nothing]
|
||||
-- Nothing
|
||||
-- >>> andM [putStrLn "1" >> pure True, putStrLn "2" >> pure False, putStrLn "3" >> undefined]
|
||||
-- >>> andM [putTextLn "1" >> pure True, putTextLn "2" >> pure False, putTextLn "3" >> pure True]
|
||||
-- 1
|
||||
-- 2
|
||||
-- False
|
||||
|
@ -23,6 +23,8 @@ import Data.Maybe (Maybe (..), maybe)
|
||||
import Universum.Applicative (pass)
|
||||
import Universum.Monad.Reexport (Either (..), either)
|
||||
|
||||
-- $setup
|
||||
-- >>> import Universum.Bool (Bool (..))
|
||||
|
||||
-- | Extracts value from 'Left' or return given default value.
|
||||
--
|
||||
@ -57,7 +59,7 @@ leftToMaybe = either Just (const Nothing)
|
||||
--
|
||||
-- >>> rightToMaybe (Left True)
|
||||
-- Nothing
|
||||
-- >>> leftToMaybe (Right "aba")
|
||||
-- >>> rightToMaybe (Right "aba")
|
||||
-- Just "aba"
|
||||
rightToMaybe :: Either l r -> Maybe r
|
||||
rightToMaybe = either (const Nothing) Just
|
||||
@ -75,7 +77,7 @@ maybeToRight l = maybe (Left l) Right
|
||||
--
|
||||
-- >>> maybeToLeft True (Just "aba")
|
||||
-- Left "aba"
|
||||
-- >>> maybeToRight True Nothing
|
||||
-- >>> maybeToLeft True Nothing
|
||||
-- Right True
|
||||
maybeToLeft :: r -> Maybe l -> Either l r
|
||||
maybeToLeft r = maybe (Right r) Left
|
||||
|
@ -14,6 +14,11 @@ module Universum.Monad.Maybe
|
||||
import Universum.Applicative (Applicative, pass, pure)
|
||||
import Universum.Monad.Reexport (Maybe (..), Monad (..))
|
||||
|
||||
-- $setup
|
||||
-- >>> import Universum.Bool (Bool (..))
|
||||
-- >>> import Universum.Function (($))
|
||||
-- >>> import Universum.Print (putTextLn)
|
||||
|
||||
-- | Specialized version of 'for_' for 'Maybe'. It's used for code readability.
|
||||
-- Also helps to avoid space leaks:
|
||||
-- <http://www.snoyman.com/blog/2017/01/foldable-mapm-maybe-and-recursive-functions Foldable.mapM_ space leak>.
|
||||
@ -42,9 +47,9 @@ whenNothing Nothing m = m
|
||||
-- | Performs default 'Applicative' action if 'Nothing' is given.
|
||||
-- Do nothing for 'Just'. Convenient for discarding 'Just' content.
|
||||
--
|
||||
-- >>> whenNothing_ Nothing $ putText "Nothing!"
|
||||
-- >>> whenNothing_ Nothing $ putTextLn "Nothing!"
|
||||
-- Nothing!
|
||||
-- >>> whenNothing_ (Just True) $ putText "Nothing!"
|
||||
-- >>> whenNothing_ (Just True) $ putTextLn "Nothing!"
|
||||
whenNothing_ :: Applicative f => Maybe a -> f () -> f ()
|
||||
whenNothing_ Nothing m = m
|
||||
whenNothing_ _ _ = pass
|
||||
|
@ -23,6 +23,10 @@ import Data.Semigroup (Option (..), Semigroup (sconcat, stimes, (<>)), WrappedMo
|
||||
|
||||
import Universum.Monad.Reexport (Maybe, fromMaybe)
|
||||
|
||||
-- $setup
|
||||
-- >>> import Universum.Base (Int)
|
||||
-- >>> import Universum.Monad (Maybe (..))
|
||||
|
||||
-- | Extracts 'Monoid' value from 'Maybe' returning 'mempty' if 'Nothing'.
|
||||
--
|
||||
-- >>> maybeToMonoid (Just [1,2,3] :: Maybe [Int])
|
||||
|
@ -39,7 +39,7 @@ import qualified Data.Set as Set
|
||||
-- | Like 'Prelude.nub' but runs in @O(n * log n)@ time and requires 'Ord'.
|
||||
--
|
||||
-- >>> ordNub [3, 3, 3, 2, 2, -1, 1]
|
||||
-- [3, 2, -1, 1]
|
||||
-- [3,2,-1,1]
|
||||
ordNub :: (Ord a) => [a] -> [a]
|
||||
ordNub = go Set.empty
|
||||
where
|
||||
@ -52,7 +52,7 @@ ordNub = go Set.empty
|
||||
-- | Like 'Prelude.nub' but runs in @O(n * log_16(n))@ time and requires 'Hashable'.
|
||||
--
|
||||
-- >>> hashNub [3, 3, 3, 2, 2, -1, 1]
|
||||
-- [3, 2, -1, 1]
|
||||
-- [3,2,-1,1]
|
||||
hashNub :: (Eq a, Hashable a) => [a] -> [a]
|
||||
hashNub = go HashSet.empty
|
||||
where
|
||||
@ -65,13 +65,13 @@ hashNub = go HashSet.empty
|
||||
-- | Like 'ordNub' but also sorts a list.
|
||||
--
|
||||
-- >>> sortNub [3, 3, 3, 2, 2, -1, 1]
|
||||
-- [-1, 1, 2, 3]
|
||||
-- [-1,1,2,3]
|
||||
sortNub :: (Ord a) => [a] -> [a]
|
||||
sortNub = Set.toList . Set.fromList
|
||||
|
||||
-- | Like 'hashNub' but has better performance and also doesn't save the order.
|
||||
--
|
||||
-- >>> unstableNub [3, 3, 3, 2, 2, -1, 1]
|
||||
-- [1, 2, 3, -1]
|
||||
-- [1,2,3,-1]
|
||||
unstableNub :: (Eq a, Hashable a) => [a] -> [a]
|
||||
unstableNub = HashSet.toList . HashSet.fromList
|
||||
|
@ -44,6 +44,12 @@ import qualified Text.Read (readEither)
|
||||
|
||||
import qualified GHC.Show as Show (Show (show))
|
||||
|
||||
-- $setup
|
||||
-- >>> :set -XTypeApplications -XOverloadedStrings
|
||||
-- >>> import Universum.Base (Int)
|
||||
-- >>> import Universum.Function (($))
|
||||
-- >>> import Universum.Print (putStrLn)
|
||||
|
||||
-- | Type synonym for 'Data.Text.Lazy.Text'.
|
||||
type LText = LT.Text
|
||||
|
||||
|
@ -11,6 +11,12 @@ module Universum.VarArg
|
||||
( SuperComposition(..)
|
||||
) where
|
||||
|
||||
-- $setup
|
||||
-- >>> import Universum.Base ((+))
|
||||
-- >>> import Universum.Container (null)
|
||||
-- >>> import Prelude (show)
|
||||
-- >>> import Data.List (zip5)
|
||||
|
||||
class SuperComposition a b c | a b -> c where
|
||||
-- | Allows to apply function to result of another function with multiple
|
||||
-- arguments.
|
||||
|
@ -1,9 +1,5 @@
|
||||
resolver: lts-10.0
|
||||
resolver: lts-10.3
|
||||
packages: [.]
|
||||
|
||||
nix:
|
||||
packages: [binutils, gmp]
|
||||
|
||||
extra-deps: []
|
||||
flags: {}
|
||||
extra-package-dbs: []
|
||||
|
9
test/Doctest.hs
Normal file
9
test/Doctest.hs
Normal file
@ -0,0 +1,9 @@
|
||||
module Main (main) where
|
||||
|
||||
import System.FilePath.Glob (glob)
|
||||
import Test.DocTest (doctest)
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
sourceFiles <- glob "src/**/*.hs"
|
||||
doctest $ "-XNoImplicitPrelude" : sourceFiles
|
@ -1,13 +1,13 @@
|
||||
name: universum
|
||||
version: 1.0.3
|
||||
version: 1.0.4
|
||||
synopsis: Custom prelude used in Serokell
|
||||
description: Custom prelude used in Serokell
|
||||
description: See README.md file for more details.
|
||||
homepage: https://github.com/serokell/universum
|
||||
license: MIT
|
||||
license-file: LICENSE
|
||||
author: Stephen Diehl, @serokell
|
||||
maintainer: Serokell <hi@serokell.io>
|
||||
copyright: 2016 Stephen Diehl, 2016-2017 Serokell
|
||||
copyright: 2016 Stephen Diehl, 2016-2018 Serokell
|
||||
category: Prelude
|
||||
stability: stable
|
||||
build-type: Simple
|
||||
@ -92,6 +92,17 @@ library
|
||||
default-extensions: NoImplicitPrelude
|
||||
OverloadedStrings
|
||||
|
||||
test-suite universum-doctest
|
||||
type: exitcode-stdio-1.0
|
||||
hs-source-dirs: test
|
||||
main-is: Doctest.hs
|
||||
|
||||
build-depends: base >= 4.8 && < 5
|
||||
, doctest
|
||||
, Glob
|
||||
ghc-options: -threaded
|
||||
default-language: Haskell2010
|
||||
|
||||
benchmark universum-benchmark
|
||||
type: exitcode-stdio-1.0
|
||||
default-language: Haskell2010
|
||||
|
Loading…
Reference in New Issue
Block a user