gambler 0.4.0.0 → 0.4.1.0
raw patch · 30 files changed
+309/−55 lines, 30 filesdep ~hspecPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: hspec
API changes (from Hackage documentation)
+ Fold.Nonempty: repeatedly :: forall x xs result. (forall b. NonemptyFold x b -> xs -> b) -> NonemptyFold x result -> NonemptyFold xs result
+ Fold.Nonempty.Utilities: repeatedly :: forall x xs result. (forall b. NonemptyFold x b -> xs -> b) -> NonemptyFold x result -> NonemptyFold xs result
+ Fold.Pure: repeatedly :: forall x xs result. (forall b. Fold x b -> xs -> b) -> Fold x result -> Fold xs result
+ Fold.Pure.Utilities: repeatedly :: forall x xs result. (forall b. Fold x b -> xs -> b) -> Fold x result -> Fold xs result
+ Fold.Shortcut: Alive :: Will -> b -> Vitality a b
+ Fold.Shortcut: Ambivalent :: Will
+ Fold.Shortcut: Dead :: a -> Vitality a b
+ Fold.Shortcut: Tenacious :: Will
+ Fold.Shortcut: data Vitality a b
+ Fold.Shortcut: data Will
+ Fold.Shortcut: motivate :: ShortcutFold a b -> ShortcutFold a b
+ Fold.Shortcut: premap :: (a -> b) -> ShortcutFold b r -> ShortcutFold a r
+ Fold.Shortcut: repeatedly :: forall x xs result. (forall b. ShortcutFold x b -> xs -> b) -> ShortcutFold x result -> ShortcutFold xs result
+ Fold.Shortcut: type Vitality' a = Vitality a a
+ Fold.Shortcut: withVitality :: ShortcutFold a b -> ShortcutFold a (Vitality' b)
+ Fold.Shortcut.Type: type Vitality' a = Vitality a a
+ Fold.Shortcut.Utilities: motivate :: ShortcutFold a b -> ShortcutFold a b
+ Fold.Shortcut.Utilities: premap :: (a -> b) -> ShortcutFold b r -> ShortcutFold a r
+ Fold.Shortcut.Utilities: repeatedly :: forall x xs result. (forall b. ShortcutFold x b -> xs -> b) -> ShortcutFold x result -> ShortcutFold xs result
+ Fold.Shortcut.Utilities: withVitality :: ShortcutFold a b -> ShortcutFold a (Vitality' b)
+ Fold.ShortcutNonempty: Alive :: Will -> b -> Vitality a b
+ Fold.ShortcutNonempty: Ambivalent :: Will
+ Fold.ShortcutNonempty: Dead :: a -> Vitality a b
+ Fold.ShortcutNonempty: Tenacious :: Will
+ Fold.ShortcutNonempty: data Vitality a b
+ Fold.ShortcutNonempty: data Will
+ Fold.ShortcutNonempty: motivate :: ShortcutNonemptyFold a b -> ShortcutNonemptyFold a b
+ Fold.ShortcutNonempty: premap :: (a -> b) -> ShortcutNonemptyFold b r -> ShortcutNonemptyFold a r
+ Fold.ShortcutNonempty: repeatedly :: forall x xs result. (forall b. ShortcutNonemptyFold x b -> xs -> b) -> ShortcutNonemptyFold x result -> ShortcutNonemptyFold xs result
+ Fold.ShortcutNonempty: type Vitality' a = Vitality a a
+ Fold.ShortcutNonempty: withVitality :: ShortcutNonemptyFold a b -> ShortcutNonemptyFold a (Vitality' b)
+ Fold.ShortcutNonempty.Type: type Vitality' a = Vitality a a
+ Fold.ShortcutNonempty.Utilities: motivate :: ShortcutNonemptyFold a b -> ShortcutNonemptyFold a b
+ Fold.ShortcutNonempty.Utilities: premap :: (a -> b) -> ShortcutNonemptyFold b r -> ShortcutNonemptyFold a r
+ Fold.ShortcutNonempty.Utilities: repeatedly :: forall x xs result. (forall b. ShortcutNonemptyFold x b -> xs -> b) -> ShortcutNonemptyFold x result -> ShortcutNonemptyFold xs result
+ Fold.ShortcutNonempty.Utilities: withVitality :: ShortcutNonemptyFold a b -> ShortcutNonemptyFold a (Vitality' b)
Files
- changelog.md +14/−0
- gambler.cabal +2/−2
- source/Fold.hs +5/−5
- source/Fold/Effectful/Conversion.hs +1/−1
- source/Fold/Effectful/Examples/Boring.hs +1/−1
- source/Fold/Effectful/Examples/Interesting.hs +1/−1
- source/Fold/Effectful/Run.hs +1/−1
- source/Fold/Effectful/Type.hs +1/−1
- source/Fold/Effectful/Utilities.hs +2/−2
- source/Fold/Nonempty.hs +1/−1
- source/Fold/Nonempty/Conversion.hs +2/−2
- source/Fold/Nonempty/Examples/Interesting.hs +1/−1
- source/Fold/Nonempty/Type.hs +1/−1
- source/Fold/Nonempty/Utilities.hs +19/−2
- source/Fold/Pure.hs +1/−1
- source/Fold/Pure/Conversion.hs +1/−1
- source/Fold/Pure/Run.hs +1/−1
- source/Fold/Pure/Type.hs +1/−1
- source/Fold/Pure/Utilities.hs +16/−1
- source/Fold/Shortcut.hs +2/−2
- source/Fold/Shortcut/Type.hs +4/−4
- source/Fold/Shortcut/Utilities.hs +40/−2
- source/Fold/ShortcutNonempty.hs +2/−2
- source/Fold/ShortcutNonempty/Type.hs +4/−4
- source/Fold/ShortcutNonempty/Utilities.hs +43/−2
- source/Strict.hs +16/−4
- test/Spec/Nonempty.hs +23/−4
- test/Spec/Pure.hs +20/−2
- test/Spec/Shortcut.hs +42/−2
- test/Spec/ShortcutNonempty.hs +41/−1
changelog.md view
@@ -1,3 +1,17 @@+## 0.4.1.0 (2023-03-09)++`Will (..)` and `Vitality (..)` are now re-exported from+`Fold.Shortcut` and `Fold.ShortcutNonempty`; previously these+were only available from `Fold.Shortcut.Type` and+`Fold.ShortcutNonempty.Type`.++A new utility `repeatedly` has been added to `Fold.Pure`,+`Fold.Nonempty`, `Fold.Shortcut`, and `Fold.ShortcutNonempty`.++New utilities `motivate`, `premap`, and `withVitality` and a new+type alias `Vitality'` have been added to `Fold.Shortcut` and+`Fold.ShortcutNonempty`.+ ## 0.4.0.0 (2023-03-08) Changed `ShortcutFold` from
gambler.cabal view
@@ -1,7 +1,7 @@ cabal-version: 3.0 name: gambler-version: 0.4.0.0+version: 0.4.1.0 category: Streaming synopsis: Composable, streaming, and efficient left folds@@ -9,7 +9,7 @@ memory, and you can combine folds using @Applicative@ style to derive new folds that still traverse the list only once. -author: Gabriella Gonzalez+author: Gabriella Gonzalez, Chris Martin maintainer: Chris Martin, Julie Moronuki license: BSD-3-Clause
source/Fold.hs view
@@ -73,7 +73,7 @@ effectfulToNonempty = ConvertTo.Nonempty.effectfulFold {-| Turn a fold that requires at least one input into a fold that returns-'Data.Maybe.Nothing' when there are no inputs -}+ 'Data.Maybe.Nothing' when there are no inputs -} nonemptyToEmpty :: NonemptyFold a b -> Fold a (Maybe b) nonemptyToEmpty = ConvertTo.Pure.nonemptyFold @@ -86,7 +86,7 @@ effectfulToPure = ConvertTo.Pure.effectfulFold {-| Turn a nonempty fold that requires at least one input into a fold that-returns 'Data.Maybe.Nothing' when there are no inputs -}+ returns 'Data.Maybe.Nothing' when there are no inputs -} nonemptyToEffectful :: Monad m => NonemptyFold a b -> EffectfulFold m a (Maybe b) nonemptyToEffectful = ConvertTo.Effectful.nonemptyFold@@ -100,16 +100,16 @@ reverseNonemptyList = Nonempty.reverseList {-| Allows to continue feeding a fold even after passing it to a function-that closes it -}+ that closes it -} duplicateFold :: Fold a b -> Fold a (Fold a b) duplicateFold = Pure.duplicate {-| Allows to continue feeding a fold even after passing it to a function-that closes it -}+ that closes it -} duplicateNonemptyFold :: NonemptyFold a b -> NonemptyFold a (Fold a b) duplicateNonemptyFold = Nonempty.duplicate {-| Allows to continue feeding an effectful fold even after passing it to a-function that closes it -}+ function that closes it -} duplicateEffectfulFold :: Applicative m => EffectfulFold m a b -> EffectfulFold m a (EffectfulFold m a b) duplicateEffectfulFold = Effectful.duplicate
source/Fold/Effectful/Conversion.hs view
@@ -22,7 +22,7 @@ } {-| Turn a nonempty fold that requires at least one input into a fold that-returns 'Data.Maybe.Nothing' when there are no inputs -}+ returns 'Data.Maybe.Nothing' when there are no inputs -} nonemptyFold :: Monad m => NonemptyFold a b -> EffectfulFold m a (Maybe b) nonemptyFold x = fold (Pure.nonemptyFold x)
source/Fold/Effectful/Examples/Boring.hs view
@@ -32,7 +32,7 @@ import qualified Fold.Effectful.Conversion as Convert {-| Start with the first input, append each new input on the right-with the given function -}+ with the given function -} magma :: (a -> a -> a) -> Monad m => EffectfulFold m a (Maybe a) magma step = Convert.nonemptyFold (Nonempty.magma step)
source/Fold/Effectful/Examples/Interesting.hs view
@@ -16,7 +16,7 @@ effect f = effectMonoid (\a -> void (f a)) {-| Performs an action for each input, monoidally combining the results-from all the actions. -}+ from all the actions -} effectMonoid :: (Monoid w, Monad m) => (a -> m w) -> EffectfulFold m a w effectMonoid act = EffectfulFold { initial = Applicative.pure mempty
source/Fold/Effectful/Run.hs view
@@ -9,7 +9,7 @@ import qualified Data.Foldable as F {-| Fold an listlike container to an action that produces a single summary-result -}+ result -} run :: Foldable f => Monad m => EffectfulFold m a b -> f a -> m b run EffectfulFold{ initial, step, extract } as0 = do x0 <- initial
source/Fold/Effectful/Type.hs view
@@ -9,7 +9,7 @@ import qualified Strict -{- | Processes inputs of type @a@ and results in an effectful value of type @m b@ -}+{-| Processes inputs of type @a@ and results in an effectful value of type @m b@ -} data EffectfulFold m a b = forall x. EffectfulFold { initial :: m x , step :: x -> a -> m x
source/Fold/Effectful/Utilities.hs view
@@ -10,7 +10,7 @@ import Prelude ((-)) {-| Shift an effectful fold from one monad to another with a morphism such as-@lift@ or @liftIO@ -}+ @lift@ or @liftIO@ -} hoist :: (forall x . m x -> n x) -> EffectfulFold m a b -> EffectfulFold n a b hoist f EffectfulFold{ initial, step, extract } = EffectfulFold { initial = f initial@@ -19,7 +19,7 @@ } {-| Allows to continue feeding an effectful fold even after passing it to a-function that closes it -}+ function that closes it -} duplicate :: Applicative m => EffectfulFold m a b -> EffectfulFold m a (EffectfulFold m a b) duplicate EffectfulFold{ initial, step, extract } = EffectfulFold { initial
source/Fold/Nonempty.hs view
@@ -17,7 +17,7 @@ {- * Conversion -} fold, effectfulFold, shortcutFold, shortcutNonemptyFold, - {- * Utilities -} duplicate, premap, nest,+ {- * Utilities -} duplicate, repeatedly, premap, nest, ) where
source/Fold/Nonempty/Conversion.hs view
@@ -15,13 +15,13 @@ import qualified Fold.ShortcutNonempty.Type as ShortcutNonempty {-| Turn a regular fold that allows empty input into a fold that-requires at least one input -}+ requires at least one input -} fold :: Fold a b -> NonemptyFold a b fold Fold{ Fold.initial, Fold.step, Fold.extract } = NonemptyFold{ initial = step initial, step, extract } {-| Turn an effectful fold into a pure fold that requires at least-one input -}+ one input -} effectfulFold :: EffectfulFold Identity a b -> NonemptyFold a b effectfulFold x = fold (Fold.Conversion.effectfulFold x)
source/Fold/Nonempty/Examples/Interesting.hs view
@@ -19,7 +19,7 @@ import qualified Strict {-| Start with the first input, append each new input on the right-with the given function -}+ with the given function -} magma :: (a -> a -> a) -> NonemptyFold a a magma step = NonemptyFold{ initial = id, step, extract = id }
source/Fold/Nonempty/Type.hs view
@@ -7,7 +7,7 @@ import qualified Strict -{- | Processes at least one input of type @a@ and results in a value of type @b@ -}+{-| Processes at least one input of type @a@ and results in a value of type @b@ -} data NonemptyFold a b = forall x. NonemptyFold { initial :: a -> x , step :: x -> a -> x
source/Fold/Nonempty/Utilities.hs view
@@ -7,15 +7,18 @@ import Fold.Pure.Type (Fold (Fold)) import qualified Fold.Pure.Type as Pure+import qualified Fold.Pure.Utilities as Pure+import qualified Fold.Pure.Run as Pure.Run+import qualified Fold.Nonempty.Conversion as Nonempty {-| Allows to continue feeding a fold even after passing it to a function-that closes it -}+ that closes it -} duplicate :: NonemptyFold a b -> NonemptyFold a (Fold a b) duplicate NonemptyFold{ initial, step, extract } = NonemptyFold{ initial, step, extract = \x -> Fold { Pure.initial = x, Pure.step, Pure.extract } } -{-| @(premap f folder)@ returns a new fold where @f@ is applied at each step -}+{-| Applies a function to each input before processing -} premap :: (a -> b) -> NonemptyFold b r -> NonemptyFold a r premap f NonemptyFold{ initial, step, extract } = NonemptyFold{ initial = \a -> initial (f a),@@ -25,3 +28,17 @@ nest :: Applicative f => NonemptyFold a b -> NonemptyFold (f a) (f b) nest NonemptyFold{ initial, step, extract } = NonemptyFold { initial = fmap initial, step = liftA2 step, extract = fmap extract }++{-| Convert a nonempty fold for a single item (@x@) into a+ nonempty fold for nonempty lists of items (@xs@) -}+repeatedly :: forall x xs result.+ (forall b. NonemptyFold x b -> xs -> b)+ -- ^ A witness to the fact that @xs@ is a nonempty list of @x@+ -> NonemptyFold x result+ -> NonemptyFold xs result+repeatedly runXs foldX =+ NonemptyFold+ { initial = runXs (duplicate foldX)+ , step = \f -> runXs (Nonempty.fold (Pure.duplicate f))+ , extract = \f -> Pure.Run.run f []+ }
source/Fold/Pure.hs view
@@ -17,7 +17,7 @@ {- * Conversion -} effectfulFold, nonemptyFold, shortcutFold, shortcutNonemptyFold, - {- * Utilities -} duplicate, premap, prefilter, predropWhile, drop, nest,+ {- * Utilities -} duplicate, repeatedly, premap, prefilter, predropWhile, drop, nest, ) where
source/Fold/Pure/Conversion.hs view
@@ -30,7 +30,7 @@ } {-| Turn a fold that requires at least one input into a fold that returns-'Data.Maybe.Nothing' when there are no inputs -}+ 'Data.Maybe.Nothing' when there are no inputs -} nonemptyFold :: NonemptyFold a b -> Fold a (Maybe b) nonemptyFold NonemptyFold{ Nonempty.initial, Nonempty.step, Nonempty.extract } =
source/Fold/Pure/Run.hs view
@@ -19,7 +19,7 @@ cons a k x = k $! step x a {-| Rather than only obtain a single final result, scanning gives a running-total that shows the intermediate result at each step along the way+ total that shows the intermediate result at each step along the way @ scan 'Fold.Pure.Examples.monoid' ["a", "b", "c"] = ["","a","ab","abc"]
source/Fold/Pure/Type.hs view
@@ -7,7 +7,7 @@ import qualified Strict -{- | Processes inputs of type @a@ and results in a value of type @b@ -}+{-| Processes inputs of type @a@ and results in a value of type @b@ -} data Fold a b = forall x. Fold { initial :: x , step :: x -> a -> x
source/Fold/Pure/Utilities.hs view
@@ -5,13 +5,14 @@ import Control.Applicative (Applicative, liftA2, pure) import Data.Bool (Bool (False, True), (&&)) import Data.Functor (fmap)+import Fold.Pure.Run (run) import Numeric.Natural (Natural) import Prelude ((-)) import qualified Strict {-| Allows to continue feeding a fold even after passing it to a function-that closes it -}+ that closes it -} duplicate :: Fold a b -> Fold a (Fold a b) duplicate Fold{ initial, step, extract } = Fold{ initial, step, extract = \x -> Fold{ initial = x, step, extract } }@@ -51,3 +52,17 @@ nest :: Applicative f => Fold a b -> Fold (f a) (f b) nest Fold{ initial, step, extract } = Fold { initial = pure initial, step = liftA2 step, extract = fmap extract }++{-| Convert a fold for a single item (@x@) into a fold for+ lists of items (@xs@) -}+repeatedly :: forall x xs result.+ (forall b. Fold x b -> xs -> b)+ -- ^ A witness to the fact that @xs@ is a list of @x@+ -> Fold x result+ -> Fold xs result+repeatedly runXs foldX =+ Fold+ { initial = run (duplicate foldX) []+ , step = \f -> runXs (duplicate f)+ , extract = \f -> run f []+ }
source/Fold/Shortcut.hs view
@@ -1,6 +1,6 @@ module Fold.Shortcut (- {- * Type -} ShortcutFold (..),+ {- * Type -} ShortcutFold (..), Will (..), Vitality (..), Vitality', {- * Run -} run, @@ -17,7 +17,7 @@ {- * Conversion -} fold, effectfulFold, nonemptyFold, shortcutNonemptyFold, - {- * Utilities -} demotivate, duplicate,+ {- * Utilities -} motivate, demotivate, duplicate, repeatedly, premap, withVitality, ) where
source/Fold/Shortcut/Type.hs view
@@ -1,7 +1,7 @@ module Fold.Shortcut.Type ( ShortcutFold (..),- Will (..), Vitality (..),+ Will (..), Vitality (..), Vitality', ) where @@ -9,13 +9,13 @@ import Data.Functor (Functor, fmap) import Data.Monoid (Monoid, mempty) import Data.Semigroup (Semigroup, (<>))-import Strict (Will (..), Vitality (..))+import Strict (Will (..), Vitality (..), Vitality') import Data.Void (absurd) import qualified Strict -{- | Processes inputs of type @a@, has the ability to halt midway- through the stream, and results in a value of type @b@ -}+{-| Processes inputs of type @a@, has the ability to halt midway+ through the stream, and results in a value of type @b@ -} data ShortcutFold a b = forall x y. ShortcutFold { initial :: Vitality x y , step :: y -> a -> Vitality x y
source/Fold/Shortcut/Utilities.hs view
@@ -2,10 +2,19 @@ import Fold.Shortcut.Type -import Strict (willSave)+import Fold.Shortcut.Run (run)+import Strict (willSave, willBoost, getVitality') import qualified Strict +motivate :: ShortcutFold a b -> ShortcutFold a b+motivate ShortcutFold{ initial, step, extract } =+ ShortcutFold+ { initial = willBoost initial+ , step = \x a -> willBoost (step x a)+ , extract+ }+ {-| Causes a shortcut fold to stop once it becomes ambivalent -} demotivate :: ShortcutFold a b -> ShortcutFold a b demotivate ShortcutFold{ initial, step, extract } =@@ -20,7 +29,7 @@ } {-| Allows to continue feeding a fold even after passing it to a function-that closes it -}+ that closes it -} duplicate :: ShortcutFold a b -> ShortcutFold a (ShortcutFold a b) duplicate ShortcutFold{ initial, step, extract } = ShortcutFold@@ -28,3 +37,32 @@ , step , extract = \v -> ShortcutFold{ initial = v, step, extract } }++withVitality :: ShortcutFold a b -> ShortcutFold a (Vitality' b)+withVitality ShortcutFold{ initial, step, extract } =+ ShortcutFold+ { initial+ , step+ , extract = \v -> let x = extract v in case v of+ Alive w _ -> Alive w x+ Dead _ -> Dead x+ }++{-| Convert a fold for a single item (@x@) into a fold for lists+ of items (@xs@) -}+repeatedly :: forall x xs result.+ (forall b. ShortcutFold x b -> xs -> b)+ -- ^ A witness to the fact that @xs@ is a list of @x@+ -> ShortcutFold x result+ -> ShortcutFold xs result+repeatedly runXs foldX =+ ShortcutFold+ { initial = run (withVitality (duplicate foldX)) []+ , step = \f xs -> runXs (withVitality (duplicate f)) xs+ , extract = \f -> run (getVitality' f) []+ }++{-| Applies a function to each input before processing -}+premap :: (a -> b) -> ShortcutFold b r -> ShortcutFold a r+premap f ShortcutFold{ initial, step, extract } =+ ShortcutFold{ initial, step = \x a -> step x (f a), extract }
source/Fold/ShortcutNonempty.hs view
@@ -1,6 +1,6 @@ module Fold.ShortcutNonempty (- {- * Type -} ShortcutNonemptyFold (..),+ {- * Type -} ShortcutNonemptyFold (..), Will (..), Vitality (..), Vitality', {- * Run -} run, @@ -17,7 +17,7 @@ {- * Conversion -} fold, effectfulFold, nonemptyFold, shortcutFold, - {- * Utilities -} demotivate, duplicate,+ {- * Utilities -} motivate, demotivate, duplicate, repeatedly, premap, withVitality, ) where
source/Fold/ShortcutNonempty/Type.hs view
@@ -1,7 +1,7 @@ module Fold.ShortcutNonempty.Type ( ShortcutNonemptyFold (..),- Will (..), Vitality (..),+ Will (..), Vitality (..), Vitality', ) where @@ -9,13 +9,13 @@ import Data.Functor (Functor, fmap) import Data.Monoid (Monoid, mempty) import Data.Semigroup (Semigroup, (<>))-import Strict (Will (..), Vitality (..))+import Strict (Will (..), Vitality (..), Vitality') import Data.Void (absurd) import qualified Strict -{- | Processes at least one input of type @a@, has the ability to halt- midway through the stream, and results in a value of type @b@ -}+{-| Processes at least one input of type @a@, has the ability to halt+ midway through the stream, and results in a value of type @b@ -} data ShortcutNonemptyFold a b = forall x y. ShortcutNonemptyFold { initial :: a -> Vitality x y , step :: y -> a -> Vitality x y
source/Fold/ShortcutNonempty/Utilities.hs view
@@ -3,11 +3,22 @@ import Fold.ShortcutNonempty.Type import Fold.Shortcut.Type (ShortcutFold (ShortcutFold))-import Strict (willSave)+import Strict (willSave, willBoost, getVitality') import qualified Strict import qualified Fold.Shortcut.Type as Empty+import qualified Fold.Shortcut.Utilities as Empty+import qualified Fold.Shortcut.Run as Empty (run)+import qualified Fold.ShortcutNonempty.Conversion as Nonempty +motivate :: ShortcutNonemptyFold a b -> ShortcutNonemptyFold a b+motivate ShortcutNonemptyFold{ initial, step, extract } =+ ShortcutNonemptyFold+ { initial = \a -> willBoost (initial a)+ , step = \x a -> willBoost (step x a)+ , extract+ }+ {-| Causes a shortcut fold to stop once it becomes ambivalent -} demotivate :: ShortcutNonemptyFold a b -> ShortcutNonemptyFold a b demotivate ShortcutNonemptyFold{ initial, step, extract } =@@ -22,7 +33,7 @@ } {-| Allows to continue feeding a fold even after passing it to a function-that closes it -}+ that closes it -} duplicate :: ShortcutNonemptyFold a b -> ShortcutNonemptyFold a (ShortcutFold a b) duplicate ShortcutNonemptyFold{ initial, step, extract } = ShortcutNonemptyFold@@ -30,3 +41,33 @@ , step , extract = \v -> ShortcutFold{ Empty.initial = v, Empty.step, Empty.extract } }++withVitality :: ShortcutNonemptyFold a b -> ShortcutNonemptyFold a (Vitality' b)+withVitality ShortcutNonemptyFold{ initial, step, extract } =+ ShortcutNonemptyFold+ { initial+ , step+ , extract = \v -> let x = extract v in case v of+ Alive w _ -> Alive w x+ Dead _ -> Dead x+ }++{-| Convert a nonempty fold for a single item (@x@) into a+ nonempty fold for nonempty lists of items (@xs@) -}+repeatedly :: forall x xs result.+ (forall b. ShortcutNonemptyFold x b -> xs -> b)+ -- ^ A witness to the fact that @xs@ is a nonempty list of @x@+ -> ShortcutNonemptyFold x result+ -> ShortcutNonemptyFold xs result+repeatedly runXs foldX =+ ShortcutNonemptyFold+ { initial = \xs -> runXs (withVitality (duplicate foldX)) xs+ , step = \f xs -> runXs (withVitality (Nonempty.shortcutFold (Empty.duplicate f))) xs+ , extract = \f -> Empty.run (getVitality' f) []+ }++{-| Applies a function to each input before processing -}+premap :: (a -> b) -> ShortcutNonemptyFold b r -> ShortcutNonemptyFold a r+premap f ShortcutNonemptyFold{ initial, step, extract } =+ ShortcutNonemptyFold{ initial = \a -> initial (f a),+ step = \x a -> step x (f a), extract }
source/Strict.hs view
@@ -1,14 +1,15 @@ {-# LANGUAGE StrictData #-} --- | Strict data types for use as internal--- accumulators to achieve constant space usage.+{-| Strict data types for use as internal+ accumulators to achieve constant space usage -} module Strict ( {- * Maybe -} Maybe (..), lazy, strict, {- * Either -} Either (..), hush, {- * Tuples -} Tuple2 (..), Tuple3 (..),- {- * Shortcut -} Vitality (..), Will (..),- unlessDead, vitality2, willSave, isAlive, isDead,+ {- * Shortcut -} Vitality (..), Vitality', Will (..),+ unlessDead, vitality2, willSave, willBoost,+ isAlive, isDead, getVitality', ) where @@ -69,6 +70,12 @@ Alive Ambivalent x -> Dead (Right x) Alive Tenacious x -> Alive Tenacious x +willBoost :: Vitality a b -> Vitality a b+willBoost v = case v of+ Dead x -> Dead x+ Alive Ambivalent x -> Alive Tenacious x+ Alive Tenacious x -> Alive Tenacious x+ type Vitality' a = Vitality a a vitality2 :: Vitality a1 b1 -> Vitality a2 b2@@ -83,3 +90,8 @@ isDead :: Vitality a b -> Bool isDead v = case v of { Alive _ _ -> False; Dead _ -> True }++getVitality' :: Vitality' a -> a+getVitality' v = case v of+ Dead x -> x+ Alive _ x -> x
test/Spec/Nonempty.hs view
@@ -4,10 +4,12 @@ import Test.Hspec +import Control.Applicative (liftA2) import Data.Function ((&), flip) import Data.List.NonEmpty (NonEmpty ((:|)))+import Data.Maybe (Maybe (..)) import Positive (Positive)-import Prelude (String, Integer)+import Prelude (String, Integer, (+), odd) import qualified Data.List as List import qualified Fold.Pure as Pure@@ -48,9 +50,26 @@ describe "duplicate" do it "lets a fold run in two phases" do- let a, c :: NonEmpty Integer+ let a :: NonEmpty Integer b :: [Integer] a = [1..3] b = [4..6]- c = [1..6]- (sum & duplicate & flip run a & flip Pure.run b) `shouldBe` (List.sum c)+ (sum & duplicate & flip run a & flip Pure.run b)+ `shouldBe` (List.sum a + List.sum b)++ describe "repeatedly" do+ let a, b, c :: NonEmpty Integer+ a = [2, 4]+ b = [10, 12, 5, 7, 8]+ c = [4, 5, 6]+ it "can fold a list of lists" do+ run (repeatedly run maximum) [a, b, c] `shouldBe` 12+ it "works in Applicative combination with another fold" do+ let f = liftA2 (,) (repeatedly run length) length+ run f [a, b, c] `shouldBe` (10, 3)+ it "works in Applicative combination with another 'repeatedly'" do+ let f = liftA2 (,) (repeatedly run (find odd)) (repeatedly run maximum)+ run f [a, b, c] `shouldBe` (Just 5, 12)+ it "works on an Applicative combination" do+ let f = liftA2 (,) (find odd) maximum+ run (repeatedly run f) [a, b, c] `shouldBe` (Just 5, 12)
test/Spec/Pure.hs view
@@ -4,7 +4,7 @@ import Test.Hspec -import Control.Applicative (pure, (<*>))+import Control.Applicative (pure, (<*>), liftA2) import Data.Bool (Bool (..)) import Data.Foldable (traverse_) import Data.Function (id, on, (.), (&), flip)@@ -12,7 +12,7 @@ import Data.Maybe (Maybe (Just, Nothing)) import Data.Monoid (mempty) import Data.Semigroup (Sum (Sum))-import Prelude ((>), String, Integer, (+), (*))+import Prelude ((>), String, Integer, (+), (*), odd) import qualified Data.Foldable as Foldable import qualified Data.List as List@@ -145,3 +145,21 @@ b = [4..6] c = [1..6] (sum & duplicate & flip run a & flip run b) `shouldBe` (List.sum c)++ describe "repeatedly" do+ let a, b, c :: [Integer]+ a = [2, 4]+ b = [10, 12, 5, 7, 8]+ c = [4, 5, 6]+ abc = [a, b, c] :: [[Integer]]+ it "can fold a list of lists" do+ run (repeatedly run sum) abc `shouldBe` List.sum (List.concat abc)+ it "works in Applicative combination with another fold" do+ let f = liftA2 (,) (repeatedly run length) length+ run f abc `shouldBe` (10, 3)+ it "works in Applicative combination with another 'repeatedly'" do+ let f = liftA2 (,) (repeatedly run (find odd)) (repeatedly run maximum)+ run f abc `shouldBe` (Just 5, Just 12)+ it "works on an Applicative combination" do+ let f = liftA2 (,) (find odd) maximum+ run (repeatedly run f) abc `shouldBe` (Just 5, Just 12)
test/Spec/Shortcut.hs view
@@ -4,13 +4,13 @@ import Test.Hspec -import Control.Applicative (pure, (<$>), (<*>))+import Control.Applicative (pure, (<$>), (<*>), liftA2) import Data.Bool (Bool (..)) import Data.Function ((&), flip) import Data.List ((++)) import Data.Maybe (Maybe (Just, Nothing)) import Data.Ord (Ord (..))-import Prelude (Integer, undefined)+import Prelude (Integer, undefined, odd) import qualified Data.Char as Char @@ -96,3 +96,43 @@ b = undefined f = find (>= 10) (f & duplicate & flip run a & flip run b) `shouldBe` Just 15++ describe "repeatedly" do+ it "can find an item in a list of lists" do+ let a, b, c :: [Integer]+ a = [2, 8, 6]+ b = [12, 4, 5, 16]+ c = [4, 18, 20]+ run (repeatedly run (find odd)) [a, b, c]+ `shouldBe` Just 5+ it "preserves laziness" do+ let a, b, c :: [Integer]+ a = [2, 8, 6]+ b = 12 : 4 : 5 : undefined+ c = undefined+ run (repeatedly run (find odd)) [a, b, c]+ `shouldBe` Just 5+ it "works within an Applicative combination with another 'repeatedly'" do+ let a, b, c :: [Integer]+ a = [2, 8, 6]+ b = 12 : 4 : 5 : undefined+ c = undefined+ f = liftA2 (,) (repeatedly run (find odd)) (repeatedly run (find (> 5)))+ run f [a, b, c]+ `shouldBe` (Just 5, Just 8)+ it "works on an Applicative combination of 'repeatedly's" do+ let a, b, c :: [Integer]+ a = [2, 8, 6]+ b = 12 : 4 : 5 : undefined+ c = undefined+ f = liftA2 (,) (find odd) (find (> 5))+ run (repeatedly run f) [a, b, c]+ `shouldBe` (Just 5, Just 8)+ it "works within an Applicative combination with something ambivalent" do+ let a, b, c :: [Integer]+ a = [2, 8, 6]+ b = 12 : 4 : 5 : undefined+ c = undefined+ f = liftA2 (,) (find odd) length+ run (repeatedly run f) [a, b, c]+ `shouldBe` (Just 5, 6)
test/Spec/ShortcutNonempty.hs view
@@ -12,7 +12,7 @@ import Data.Maybe (Maybe (..)) import Data.Ord (Ord (..)) import Positive (Positive)-import Prelude (Integer, undefined)+import Prelude (Integer, undefined, odd) import qualified Data.Char as Char import qualified Fold.Shortcut as Empty@@ -83,3 +83,43 @@ b = undefined f = find (>= 10) (f & duplicate & flip run a & flip Empty.run b) `shouldBe` Just 15++ describe "repeatedly" do+ it "can find an item in a list of lists" do+ let a, b, c :: NonEmpty Integer+ a = [2, 8, 6]+ b = [12, 4, 5, 16]+ c = [4, 18, 20]+ run (repeatedly run (find odd)) [a, b, c]+ `shouldBe` Just 5+ it "preserves laziness" do+ let a, b, c :: NonEmpty Integer+ a = [2, 8, 6]+ b = 12 :| 4 : 5 : undefined+ c = undefined+ run (repeatedly run (find odd)) [a, b, c]+ `shouldBe` Just 5+ it "works within an Applicative combination with another 'repeatedly'" do+ let a, b, c :: NonEmpty Integer+ a = [2, 8, 6]+ b = 12 :| 4 : 5 : undefined+ c = undefined+ f = liftA2 (,) (repeatedly run (find odd)) (repeatedly run (find (> 5)))+ run f [a, b, c]+ `shouldBe` (Just 5, Just 8)+ it "works on an Applicative combination of 'repeatedly's" do+ let a, b, c :: NonEmpty Integer+ a = [2, 8, 6]+ b = 12 :| 4 : 5 : undefined+ c = undefined+ f = liftA2 (,) (find odd) (find (> 5))+ run (repeatedly run f) [a, b, c]+ `shouldBe` (Just 5, Just 8)+ it "works within an Applicative combination with something ambivalent" do+ let a, b, c :: NonEmpty Integer+ a = [2, 8, 6]+ b = 12 :| 4 : 5 : undefined+ c = undefined+ f = liftA2 (,) (find odd) length+ run (repeatedly run f) [a, b, c]+ `shouldBe` (Just 5, 6)