pa-prelude 0.2.0.0 → 0.3.0.0
raw patch · 3 files changed
+134/−10 lines, 3 filesdep +foldldep +ghc-bootPVP ok
version bump matches the API change (PVP)
Dependencies added: foldl, ghc-boot
API changes (from Hackage documentation)
+ PossehlAnalyticsPrelude: instance PossehlAnalyticsPrelude.Lengthy Data.Vector.Vector
+ PossehlAnalyticsPrelude: instance PossehlAnalyticsPrelude.Lengthy GHC.Base.NonEmpty
+ PossehlAnalyticsPrelude: instance PossehlAnalyticsPrelude.Lengthy []
+ PossehlAnalyticsPrelude: lengthNatural :: Lengthy f => f a -> Natural
+ PossehlAnalyticsPrelude: maximumBy1 :: Foldable1 f => (a -> a -> Ordering) -> f a -> a
+ PossehlAnalyticsPrelude: minimumBy1 :: Foldable1 f => (a -> a -> Ordering) -> f a -> a
+ PossehlAnalyticsPrelude: overNonEmpty :: Applicative f => (NonEmpty a -> f [b]) -> [a] -> f [b]
+ PossehlAnalyticsPrelude: pattern IsEmpty :: [a]
+ PossehlAnalyticsPrelude: pattern IsNonEmpty :: NonEmpty a -> [a]
+ PossehlAnalyticsPrelude: stringToBytesUtf8 :: String -> ByteString
+ PossehlAnalyticsPrelude: zip3NonEmpty :: NonEmpty a -> NonEmpty b -> NonEmpty c -> NonEmpty (a, b, c)
+ PossehlAnalyticsPrelude: zip4NonEmpty :: NonEmpty a -> NonEmpty b -> NonEmpty c -> NonEmpty d -> NonEmpty (a, b, c, d)
+ PossehlAnalyticsPrelude: zipNonEmpty :: NonEmpty a -> NonEmpty b -> NonEmpty (a, b)
+ PossehlAnalyticsPrelude: zipWith3NonEmpty :: (a -> b -> c -> d) -> NonEmpty a -> NonEmpty b -> NonEmpty c -> NonEmpty d
+ PossehlAnalyticsPrelude: zipWithNonEmpty :: (a -> b -> c) -> NonEmpty a -> NonEmpty b -> NonEmpty c
- PossehlAnalyticsPrelude: class Bifunctor (p :: Type -> Type -> Type)
+ PossehlAnalyticsPrelude: class forall a. () => Functor p a => Bifunctor (p :: Type -> Type -> Type)
- PossehlAnalyticsPrelude: class Category (cat :: k -> k -> Type)
+ PossehlAnalyticsPrelude: class () => Category (cat :: k -> k -> Type)
- PossehlAnalyticsPrelude: class Contravariant (f :: Type -> Type)
+ PossehlAnalyticsPrelude: class () => Contravariant (f :: Type -> Type)
- PossehlAnalyticsPrelude: class Generic a
+ PossehlAnalyticsPrelude: class () => Generic a
- PossehlAnalyticsPrelude: class HasField (x :: k) r a | x r -> a
+ PossehlAnalyticsPrelude: class () => HasField (x :: k) r a | x r -> a
- PossehlAnalyticsPrelude: class Lift (t :: TYPE r)
+ PossehlAnalyticsPrelude: class () => Lift (t :: TYPE r)
- PossehlAnalyticsPrelude: class MonadTrans (t :: Type -> Type -> Type -> Type)
+ PossehlAnalyticsPrelude: class forall (m :: Type -> Type). Monad m => Monad t m => MonadTrans (t :: Type -> Type -> Type -> Type)
- PossehlAnalyticsPrelude: class Profunctor (p :: Type -> Type -> Type)
+ PossehlAnalyticsPrelude: class () => Profunctor (p :: Type -> Type -> Type)
- PossehlAnalyticsPrelude: class Semigroup a
+ PossehlAnalyticsPrelude: class () => Semigroup a
- PossehlAnalyticsPrelude: class Semigroupoid (c :: k -> k -> Type)
+ PossehlAnalyticsPrelude: class () => Semigroupoid (c :: k -> k -> Type)
- PossehlAnalyticsPrelude: data ByteString
+ PossehlAnalyticsPrelude: data () => ByteString
- PossehlAnalyticsPrelude: data Map k a
+ PossehlAnalyticsPrelude: data () => Map k a
- PossehlAnalyticsPrelude: data Natural
+ PossehlAnalyticsPrelude: data () => Natural
- PossehlAnalyticsPrelude: data NonEmpty a
+ PossehlAnalyticsPrelude: data () => NonEmpty a
- PossehlAnalyticsPrelude: data Proxy (t :: k)
+ PossehlAnalyticsPrelude: data () => Proxy (t :: k)
- PossehlAnalyticsPrelude: data Scientific
+ PossehlAnalyticsPrelude: data () => Scientific
- PossehlAnalyticsPrelude: data Text
+ PossehlAnalyticsPrelude: data () => Text
- PossehlAnalyticsPrelude: data These a b
+ PossehlAnalyticsPrelude: data () => These a b
- PossehlAnalyticsPrelude: data Validation e a
+ PossehlAnalyticsPrelude: data () => Validation e a
- PossehlAnalyticsPrelude: data Vector a
+ PossehlAnalyticsPrelude: data () => Vector a
- PossehlAnalyticsPrelude: data Void
+ PossehlAnalyticsPrelude: data () => Void
- PossehlAnalyticsPrelude: data Word8
+ PossehlAnalyticsPrelude: data () => Word8
- PossehlAnalyticsPrelude: newtype ExceptT e (m :: Type -> Type) a
+ PossehlAnalyticsPrelude: newtype () => ExceptT e (m :: Type -> Type) a
- PossehlAnalyticsPrelude: newtype Identity a
+ PossehlAnalyticsPrelude: newtype () => Identity a
- PossehlAnalyticsPrelude: throwM :: (MonadThrow m, Exception e) => e -> m a
+ PossehlAnalyticsPrelude: throwM :: (MonadThrow m, HasCallStack, Exception e) => e -> m a
Files
- CHANGELOG.md +8/−0
- pa-prelude.cabal +7/−1
- src/PossehlAnalyticsPrelude.hs +119/−9
CHANGELOG.md view
@@ -1,5 +1,13 @@ # Revision history for pa-prelude +## 0.2.0.0 -- 2024-09-04++- Add `IsEmpty` and `IsNonEmpty` patterns (for getting a `NonEmpty` from a match)+- Add `stringToBytesUtf8`+- Add multiple function for dealing with `NonEmpty`+- Add `lengthNatural` and a `Lengthy` class that should be preferred over `Foldable.length`+- Add `maximumBy1` and `minimumBy1`+ ## 0.2.0.0 -- 2023-10-15 - `thenValidate` is now `thenValidateM`
pa-prelude.cabal view
@@ -1,7 +1,7 @@ cabal-version: 3.0 -- !!! ATTN: file autogenerated from pa-template.cabal.mustache on toplevel !!! name: pa-prelude-version: 0.2.0.0+version: 0.3.0.0 synopsis: The Possehl Analytics Prelude description: A prelude to be used in addition to `base`’s `Prelude` license: BSD-3-Clause@@ -61,6 +61,10 @@ -- to enable the `type` keyword in import lists (ormolu uses this automatically) ExplicitNamespaces + -- allows defining pattern synonyms, but also the `import Foo (pattern FooPattern)` import syntax+ PatternSynonyms++ default-language: GHC2021 library@@ -74,6 +78,8 @@ base <5, error >0.1 && <1.1, exceptions,+ ghc-boot,+ foldl, mtl, text, PyF,
src/PossehlAnalyticsPrelude.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE ImplicitParams #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MagicHash #-}+{-# LANGUAGE ViewPatterns #-} module PossehlAnalyticsPrelude ( -- * Text conversions@@ -10,6 +11,7 @@ fmt, textToString, stringToText,+ stringToBytesUtf8, showToText, textToBytesUtf8, textToBytesUtf8Lazy,@@ -101,13 +103,24 @@ thenValidate, thenValidateM, NonEmpty ((:|)),+ pattern IsEmpty,+ pattern IsNonEmpty, singleton, nonEmpty, nonEmptyDef,+ overNonEmpty,+ zipNonEmpty,+ zipWithNonEmpty,+ zip3NonEmpty,+ zipWith3NonEmpty,+ zip4NonEmpty, toList, toNonEmptyDefault,+ lengthNatural, maximum1, minimum1,+ maximumBy1,+ minimumBy1, Vector, Generic, Lift,@@ -154,6 +167,7 @@ import Control.Applicative ((<|>)) import Control.Category (Category, (>>>))+import Control.Foldl.NonEmpty qualified as Foldl1 import Control.Monad (guard, join, unless, when) import Control.Monad.Catch (MonadThrow (throwM)) import Control.Monad.Except@@ -179,7 +193,9 @@ import Data.Functor ((<&>)) import Data.Functor.Contravariant (Contravariant (contramap), (>$<)) import Data.Functor.Identity (Identity (runIdentity))+import Data.List (zip4) import Data.List.NonEmpty (NonEmpty ((:|)), nonEmpty)+import Data.List.NonEmpty qualified as NonEmpty import Data.Map.Strict ( Map, )@@ -188,7 +204,7 @@ import Data.Maybe qualified as Maybe import Data.Profunctor (Profunctor, dimap, lmap, rmap) import Data.Scientific (Scientific)-import Data.Semigroup (Max (Max, getMax), Min (Min, getMin), sconcat)+import Data.Semigroup (sconcat) import Data.Semigroup.Foldable (Foldable1 (fold1), foldMap1) import Data.Semigroup.Traversable (Traversable1) import Data.Semigroupoid (Semigroupoid (o))@@ -211,6 +227,7 @@ import GHC.Natural (Natural) import GHC.Records (HasField) import GHC.Stack (HasCallStack)+import GHC.Utils.Encoding.UTF8 qualified as GHC import Language.Haskell.TH.Syntax (Lift) import PyF (fmt) import System.Exit qualified@@ -249,10 +266,10 @@ -- -- Specialized examples: ----- @@+-- @ -- for functions : (a -> b) -> (b -> c) -> (a -> c) -- for Folds: Fold a b -> Fold b c -> Fold a c--- @@+-- @ (&>>) :: (Semigroupoid s) => s a b -> s b c -> s a c (&>>) = flip Data.Semigroupoid.o @@ -293,26 +310,51 @@ bytesToTextUtf8LenientLazy = Data.Text.Lazy.Encoding.decodeUtf8With Data.Text.Encoding.Error.lenientDecode --- | Make a lazy text strict+-- | Make a lazy 'Text' strict. toStrict :: Data.Text.Lazy.Text -> Text toStrict = Data.Text.Lazy.toStrict --- | Make a strict text lazy+-- | Make a strict 'Text' lazy. toLazy :: Text -> Data.Text.Lazy.Text toLazy = Data.Text.Lazy.fromStrict +-- | Make a lazy 'ByteString' strict. toStrictBytes :: Data.ByteString.Lazy.ByteString -> ByteString toStrictBytes = Data.ByteString.Lazy.toStrict +-- | Make a strict 'ByteString' lazy. toLazyBytes :: ByteString -> Data.ByteString.Lazy.ByteString toLazyBytes = Data.ByteString.Lazy.fromStrict +-- | Convert a (performant) 'Text' into an (imperformant) list-of-char 'String'.+--+-- Some libraries (like @time@ or @network-uri@) still use the `String` as their interface. We only want to convert to string at the edges, otherwise use 'Text'.+--+-- ATTN: Don’t use `String` in code if you can avoid it, prefer `Text` instead. textToString :: Text -> String textToString = Data.Text.unpack +-- | Convert an (imperformant) list-of-char 'String' into a (performant) 'Text' .+--+-- Some libraries (like @time@ or @network-uri@) still use the `String` as their interface. We want to convert 'String' to 'Text' as soon as possible and only use 'Text' in our code.+--+-- ATTN: Don’t use `String` in code if you can avoid it, prefer `Text` instead. stringToText :: String -> Text stringToText = Data.Text.pack +-- | Encode a String to an UTF-8 encoded Bytestring+--+-- ATTN: Don’t use `String` in code if you can avoid it, prefer `Text` instead.+stringToBytesUtf8 :: String -> ByteString+stringToBytesUtf8 = GHC.utf8EncodeByteString++-- | Like `show`, but generate a 'Text'+--+-- ATTN: This goes via `String` and thus is fairly inefficient.+-- We should add a good display library at one point.+--+-- ATTN: unlike `show`, this forces the whole @'a+-- so only use if you want to display the whole thing. showToText :: (Show a) => a -> Text showToText = stringToText . show @@ -329,6 +371,18 @@ {-# INLINE charToWordUnsafe #-} charToWordUnsafe = fromIntegral . Data.Char.ord +pattern IsEmpty :: [a]+pattern IsEmpty <- (null -> True)+ where+ IsEmpty = []++pattern IsNonEmpty :: NonEmpty a -> [a]+pattern IsNonEmpty n <- (nonEmpty -> Just n)+ where+ IsNonEmpty n = toList n++{-# COMPLETE IsEmpty, IsNonEmpty #-}+ -- | Single element in a (non-empty) list. singleton :: a -> NonEmpty a singleton a = a :| []@@ -346,13 +400,69 @@ [] -> def :| [] (x : xs') -> x :| xs' --- | @O(n)@. Get the maximum element from a non-empty structure.+-- | If the list is not empty, run the given function with a NonEmpty list, otherwise just return []+overNonEmpty :: (Applicative f) => (NonEmpty a -> f [b]) -> [a] -> f [b]+overNonEmpty f xs = case xs of+ IsEmpty -> pure []+ IsNonEmpty xs' -> f xs'++-- | Zip two non-empty lists.+zipNonEmpty :: NonEmpty a -> NonEmpty b -> NonEmpty (a, b)+{-# INLINE zipNonEmpty #-}+zipNonEmpty ~(a :| as) ~(b :| bs) = (a, b) :| zip as bs++-- | Zip two non-empty lists, combining them with the given function+zipWithNonEmpty :: (a -> b -> c) -> NonEmpty a -> NonEmpty b -> NonEmpty c+{-# INLINE zipWithNonEmpty #-}+zipWithNonEmpty = NonEmpty.zipWith++-- | Zip three non-empty lists.+zip3NonEmpty :: NonEmpty a -> NonEmpty b -> NonEmpty c -> NonEmpty (a, b, c)+{-# INLINE zip3NonEmpty #-}+zip3NonEmpty ~(a :| as) ~(b :| bs) ~(c :| cs) = (a, b, c) :| zip3 as bs cs++-- | Zip three non-empty lists, combining them with the given function+zipWith3NonEmpty :: (a -> b -> c -> d) -> NonEmpty a -> NonEmpty b -> NonEmpty c -> NonEmpty d+{-# INLINE zipWith3NonEmpty #-}+zipWith3NonEmpty f ~(x :| xs) ~(y :| ys) ~(z :| zs) = f x y z :| zipWith3 f xs ys zs++-- | Zip four non-empty lists+zip4NonEmpty :: NonEmpty a -> NonEmpty b -> NonEmpty c -> NonEmpty d -> NonEmpty (a, b, c, d)+{-# INLINE zip4NonEmpty #-}+zip4NonEmpty ~(a :| as) ~(b :| bs) ~(c :| cs) ~(d :| ds) = (a, b, c, d) :| zip4 as bs cs ds++-- | We don’t want to use Foldable’s `length`, because it is too polymorphic and can lead to bugs.+-- Only list-y things should have a length.+class (Foldable f) => Lengthy f++instance Lengthy []++instance Lengthy NonEmpty++instance Lengthy Vector++lengthNatural :: (Lengthy f) => f a -> Natural+lengthNatural xs =+ xs+ & Foldable.length+ -- length can never be negative or something went really, really wrong+ & fromIntegral @Int @Natural++-- | @O(n)@. Get the maximum element from a non-empty structure (strict). maximum1 :: (Foldable1 f, Ord a) => f a -> a-maximum1 xs = xs & foldMap1 Max & getMax+maximum1 = Foldl1.fold1 Foldl1.maximum --- | @O(n)@. Get the minimum element from a non-empty structure.+-- | @O(n)@. Get the maximum element from a non-empty structure, using the given comparator (strict).+maximumBy1 :: (Foldable1 f) => (a -> a -> Ordering) -> f a -> a+maximumBy1 f = Foldl1.fold1 (Foldl1.maximumBy f)++-- | @O(n)@. Get the minimum element from a non-empty structure (strict). minimum1 :: (Foldable1 f, Ord a) => f a -> a-minimum1 xs = xs & foldMap1 Min & getMin+minimum1 = Foldl1.fold1 Foldl1.minimum++-- | @O(n)@. Get the minimum element from a non-empty structure, using the given comparator (strict).+minimumBy1 :: (Foldable1 f) => (a -> a -> Ordering) -> f a -> a+minimumBy1 f = Foldl1.fold1 (Foldl1.minimumBy f) -- | Annotate a 'Maybe' with an error message and turn it into an 'Either'. annotate :: err -> Maybe a -> Either err a