ref: cb7ae3b919c7ab22cd6cb6003b6b63a5931a6402
dir: /lib/Data/Foldable1.hs/
-- |
-- Copyright: Edward Kmett, Oleg Grenrus
-- License: BSD-3-Clause
--
-- A class of non-empty data structures that can be folded to a summary value.
--
-- @since 4.18.0.0
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE Trustworthy #-}
{-# LANGUAGE TypeOperators #-}
module Data.Foldable1 (
Foldable1(..),
foldr1, foldr1',
foldl1, foldl1',
intercalate1,
foldrM1,
foldlM1,
foldrMapM1,
foldlMapM1,
maximumBy,
minimumBy,
) where
import Data.Foldable (Foldable, foldlM, foldr)
import Data.List ([](..), foldl, foldl')
import Data.List.NonEmpty (NonEmpty (..))
import Data.Maybe
import Data.Semigroup
import Data.Tuple (Solo (..))
import Prelude
(Maybe (..), Monad (..), Ord, Ordering (..), id, seq, ($!), ($), (.),
(=<<), flip, const, error)
import qualified Data.List.NonEmpty as NE
import Data.Complex (Complex (..))
import Data.Ord (Down (..))
import qualified Data.Monoid as Mon
-- Instances
import Data.Functor.Compose (Compose (..))
import Data.Functor.Identity (Identity (..))
import qualified Data.Functor.Product as Functor
import qualified Data.Functor.Sum as Functor
-- coerce
--import GHC.Internal.Data.Coerce (Coercible, coerce)
-- $setup
-- >>> import Prelude hiding (foldr1, foldl1, head, last, minimum, maximum)
-------------------------------------------------------------------------------
-- Foldable1 type class
-------------------------------------------------------------------------------
-- | Non-empty data structures that can be folded.
--
-- @since 4.18.0.0
class Foldable t => Foldable1 t where
{-# MINIMAL foldMap1 | foldrMap1 #-}
-- At some point during design it was possible to define this class using
-- only 'toNonEmpty'. But it seems a bad idea in general.
--
-- So currently we require either foldMap1 or foldrMap1
--
-- * foldMap1 defined using foldrMap1
-- * foldrMap1 defined using foldMap1
--
-- One can always define an instance using the following pattern:
--
-- toNonEmpty = ...
-- foldMap f = foldMap f . toNonEmpty
-- foldrMap1 f g = foldrMap1 f g . toNonEmpty
-- | Given a structure with elements whose type is a 'Semigroup', combine
-- them via the semigroup's @('<>')@ operator. This fold is
-- right-associative and lazy in the accumulator. When you need a strict
-- left-associative fold, use 'foldMap1'' instead, with 'id' as the map.
--
-- @since 4.18.0.0
fold1 :: Semigroup m => t m -> m
fold1 = foldMap1 id
-- | Map each element of the structure to a semigroup, and combine the
-- results with @('<>')@. This fold is right-associative and lazy in the
-- accumulator. For strict left-associative folds consider 'foldMap1''
-- instead.
--
-- >>> foldMap1 (:[]) (1 :| [2, 3, 4])
-- [1,2,3,4]
--
-- @since 4.18.0.0
foldMap1 :: Semigroup m => (a -> m) -> t a -> m
foldMap1 f = foldrMap1 f (\a m -> f a <> m)
-- | A left-associative variant of 'foldMap1' that is strict in the
-- accumulator. Use this for strict reduction when partial results are
-- merged via @('<>')@.
--
-- >>> foldMap1' Sum (1 :| [2, 3, 4])
-- Sum {getSum = 10}
--
-- @since 4.18.0.0
foldMap1' :: Semigroup m => (a -> m) -> t a -> m
foldMap1' f = foldlMap1' f (\m a -> m <> f a)
-- | 'NonEmpty' list of elements of a structure, from left to right.
--
-- >>> toNonEmpty (Identity 2)
-- 2 :| []
--
-- @since 4.18.0.0
toNonEmpty :: t a -> NonEmpty a
toNonEmpty = runNonEmptyDList . foldMap1 singleton
-- | The largest element of a non-empty structure.
--
-- >>> maximum (32 :| [64, 8, 128, 16])
-- 128
--
-- @since 4.18.0.0
maximum :: Ord a => t a -> a
maximum = getMax . foldMap1' Max
-- | The least element of a non-empty structure.
--
-- >>> minimum (32 :| [64, 8, 128, 16])
-- 8
--
-- @since 4.18.0.0
minimum :: Ord a => t a -> a
minimum = getMin . foldMap1' Min
-- | The first element of a non-empty structure.
--
-- >>> head (1 :| [2, 3, 4])
-- 1
--
-- @since 4.18.0.0
head :: t a -> a
head = getFirst . foldMap1 First
-- | The last element of a non-empty structure.
--
-- >>> last (1 :| [2, 3, 4])
-- 4
--
-- @since 4.18.0.0
last :: t a -> a
last = getLast . foldMap1 Last
-- | Right-associative fold of a structure, lazy in the accumulator.
--
-- In case of 'NonEmpty' lists, 'foldrMap1', when given a function @f@, a
-- binary operator @g@, and a list, reduces the list using @g@ from right to
-- left applying @f@ to the rightmost element:
--
-- > foldrMap1 f g (x1 :| [x2, ..., xn1, xn]) == x1 `g` (x2 `g` ... (xn1 `g` (f xn))...)
--
-- Note that since the head of the resulting expression is produced by
-- an application of @g@ to the first element of the list, if @g@ is lazy
-- in its right argument, 'foldrMap1' can produce a terminating expression
-- from an unbounded list.
--
-- For a general 'Foldable1' structure this should be semantically identical
-- to:
--
-- @foldrMap1 f g = foldrMap1 f g . 'toNonEmpty'@
--
-- @since 4.18.0.0
foldrMap1 :: (a -> b) -> (a -> b -> b) -> t a -> b
foldrMap1 f g xs =
appFromMaybe (foldMap1 (FromMaybe . h) xs) Nothing
where
h a Nothing = f a
h a (Just b) = g a b
-- | Left-associative fold of a structure but with strict application of the
-- operator.
--
-- This ensures that each step of the fold is forced to Weak Head Normal
-- Form before being applied, avoiding the collection of thunks that would
-- otherwise occur. This is often what you want to strictly reduce a
-- finite structure to a single strict result.
--
-- For a general 'Foldable1' structure this should be semantically identical
-- to:
--
-- @foldlMap1' f z = foldlMap1' f z . 'toNonEmpty'@
--
-- @since 4.18.0.0
foldlMap1' :: (a -> b) -> (b -> a -> b) -> t a -> b
foldlMap1' f g xs =
foldrMap1 f' g' xs SNothing
where
-- f' :: a -> SMaybe b -> b
f' a SNothing = f a
f' a (SJust b) = g b a
-- g' :: a -> (SMaybe b -> b) -> SMaybe b -> b
g' a x SNothing = x $! SJust (f a)
g' a x (SJust b) = x $! SJust (g b a)
-- | Left-associative fold of a structure, lazy in the accumulator. This is
-- rarely what you want, but can work well for structures with efficient
-- right-to-left sequencing and an operator that is lazy in its left
-- argument.
--
-- In case of 'NonEmpty' lists, 'foldlMap1', when given a function @f@, a
-- binary operator @g@, and a list, reduces the list using @g@ from left to
-- right applying @f@ to the leftmost element:
--
-- > foldlMap1 f g (x1 :| [x2, ..., xn]) == (...(((f x1) `g` x2) `g`...) `g` xn
--
-- Note that to produce the outermost application of the operator the entire
-- input list must be traversed. This means that 'foldlMap1' will diverge if
-- given an infinite list.
--
-- If you want an efficient strict left-fold, you probably want to use
-- 'foldlMap1'' instead of 'foldlMap1'. The reason for this is that the
-- latter does not force the /inner/ results (e.g. @(f x1) \`g\` x2@ in the
-- above example) before applying them to the operator (e.g. to
-- @(\`g\` x3)@). This results in a thunk chain \(O(n)\) elements long,
-- which then must be evaluated from the outside-in.
--
-- For a general 'Foldable1' structure this should be semantically identical
-- to:
--
-- @foldlMap1 f g = foldlMap1 f g . 'toNonEmpty'@
--
-- @since 4.18.0.0
foldlMap1 :: (a -> b) -> (b -> a -> b) -> t a -> b
foldlMap1 f g xs =
appFromMaybe (getDual (foldMap1 ((Dual . FromMaybe) . h) xs)) Nothing
where
h a Nothing = f a
h a (Just b) = g b a
-- | 'foldrMap1'' is a variant of 'foldrMap1' that performs strict reduction
-- from right to left, i.e. starting with the right-most element. The input
-- structure /must/ be finite, otherwise 'foldrMap1'' runs out of space
-- (/diverges/).
--
-- If you want a strict right fold in constant space, you need a structure
-- that supports faster than \(O(n)\) access to the right-most element.
--
-- This method does not run in constant space for structures such as
-- 'NonEmpty' lists that don't support efficient right-to-left iteration and
-- so require \(O(n)\) space to perform right-to-left reduction. Use of this
-- method with such a structure is a hint that the chosen structure may be a
-- poor fit for the task at hand. If the order in which the elements are
-- combined is not important, use 'foldlMap1'' instead.
--
-- @since 4.18.0.0
foldrMap1' :: (a -> b) -> (a -> b -> b) -> t a -> b
foldrMap1' f g xs =
foldlMap1 f' g' xs SNothing
where
f' a SNothing = f a
f' a (SJust b) = g a b
g' bb a SNothing = bb $! SJust (f a)
g' bb a (SJust b) = bb $! SJust (g a b)
-------------------------------------------------------------------------------
-- Combinators
-------------------------------------------------------------------------------
-- | A variant of 'foldrMap1' where the rightmost element maps to itself.
--
-- @since 4.18.0.0
foldr1 :: Foldable1 t => (a -> a -> a) -> t a -> a
foldr1 = foldrMap1 id
{-# INLINE foldr1 #-}
-- | A variant of 'foldrMap1'' where the rightmost element maps to itself.
--
-- @since 4.18.0.0
foldr1' :: Foldable1 t => (a -> a -> a) -> t a -> a
foldr1' = foldrMap1' id
{-# INLINE foldr1' #-}
-- | A variant of 'foldlMap1' where the leftmost element maps to itself.
--
-- @since 4.18.0.0
foldl1 :: Foldable1 t => (a -> a -> a) -> t a -> a
foldl1 = foldlMap1 id
{-# INLINE foldl1 #-}
-- | A variant of 'foldlMap1'' where the leftmost element maps to itself.
--
-- @since 4.18.0.0
foldl1' :: Foldable1 t => (a -> a -> a) -> t a -> a
foldl1' = foldlMap1' id
{-# INLINE foldl1' #-}
-- | Insert an @m@ between each pair of @t m@.
--
-- >>> intercalate1 ", " $ "hello" :| ["how", "are", "you"]
-- "hello, how, are, you"
--
-- >>> intercalate1 ", " $ "hello" :| []
-- "hello"
--
-- >>> intercalate1 mempty $ "I" :| ["Am", "Fine", "You?"]
-- "IAmFineYou?"
--
-- @since 4.18.0.0
intercalate1 :: (Foldable1 t, Semigroup m) => m -> t m -> m
intercalate1 = flip intercalateMap1 id
intercalateMap1 :: (Foldable1 t, Semigroup m) => m -> (a -> m) -> t a -> m
intercalateMap1 j f = flip joinee j . foldMap1 (JoinWith . const . f)
-- | Monadic fold over the elements of a non-empty structure,
-- associating to the right, i.e. from right to left.
--
-- @since 4.18.0.0
foldrM1 :: (Foldable1 t, Monad m) => (a -> a -> m a) -> t a -> m a
foldrM1 = foldrMapM1 return
-- | Map variant of 'foldrM1'.
--
-- @since 4.18.0.0
foldrMapM1 :: (Foldable1 t, Monad m) => (a -> m b) -> (a -> b -> m b) -> t a -> m b
foldrMapM1 g f = go . toNonEmpty
where
go (e:|es) =
case es of
[] -> g e
x:xs -> f e =<< go (x:|xs)
-- | Monadic fold over the elements of a non-empty structure,
-- associating to the left, i.e. from left to right.
--
-- @since 4.18.0.0
foldlM1 :: (Foldable1 t, Monad m) => (a -> a -> m a) -> t a -> m a
foldlM1 = foldlMapM1 return
-- | Map variant of 'foldlM1'.
--
-- @since 4.18.0.0
foldlMapM1 :: (Foldable1 t, Monad m) => (a -> m b) -> (b -> a -> m b) -> t a -> m b
foldlMapM1 g f t = g x >>= \y -> foldlM f y xs
where x:|xs = toNonEmpty t
-- | The largest element of a non-empty structure with respect to the
-- given comparison function.
--
-- @since 4.18.0.0
maximumBy :: Foldable1 t => (a -> a -> Ordering) -> t a -> a
maximumBy cmp = foldl1' max'
where max' x y = case cmp x y of
GT -> x
_ -> y
-- | The least element of a non-empty structure with respect to the
-- given comparison function.
--
-- @since 4.18.0.0
minimumBy :: Foldable1 t => (a -> a -> Ordering) -> t a -> a
minimumBy cmp = foldl1' min'
where min' x y = case cmp x y of
GT -> y
_ -> x
-------------------------------------------------------------------------------
-- Auxiliary types
-------------------------------------------------------------------------------
-- | Used for default toNonEmpty implementation.
newtype NonEmptyDList a = NEDL { unNEDL :: [a] -> NonEmpty a }
instance Semigroup (NonEmptyDList a) where
xs <> ys = NEDL (unNEDL xs . NE.toList . unNEDL ys)
{-# INLINE (<>) #-}
-- | Create dlist with a single element
singleton :: a -> NonEmptyDList a
singleton = NEDL . (:|)
-- | Convert a dlist to a non-empty list
runNonEmptyDList :: NonEmptyDList a -> NonEmpty a
runNonEmptyDList = ($ []) . unNEDL
{-# INLINE runNonEmptyDList #-}
-- | Used for foldrMap1 and foldlMap1 definitions
newtype FromMaybe b = FromMaybe { appFromMaybe :: Maybe b -> b }
instance Semigroup (FromMaybe b) where
FromMaybe f <> FromMaybe g = FromMaybe (f . Just . g)
-- | Strict maybe, used to implement default foldlMap1' etc.
data SMaybe a = SNothing | SJust !a
-- | Used to implement intercalate1/Map
newtype JoinWith a = JoinWith {joinee :: (a -> a)}
instance Semigroup a => Semigroup (JoinWith a) where
JoinWith a <> JoinWith b = JoinWith $ \j -> a j <> j <> b j
-------------------------------------------------------------------------------
-- Instances for misc base types
-------------------------------------------------------------------------------
-- | @since 4.18.0.0
instance Foldable1 NonEmpty where
foldMap1 f (x :| xs) = go (f x) xs where
go y [] = y
go y (z : zs) = y <> go (f z) zs
foldMap1' f (x :| xs) = foldl' (\m y -> m <> f y) (f x) xs
toNonEmpty = id
foldrMap1 g f (x :| xs) = go x xs where
go y [] = g y
go y (z : zs) = f y (go z zs)
foldlMap1 g f (x :| xs) = foldl f (g x) xs
foldlMap1' g f (x :| xs) = let gx = g x in gx `seq` foldl' f gx xs
head = NE.head
last = NE.last
{-
-- | @since 4.18.0.0
instance Foldable1 Down where
foldMap1 = coerce
-- | @since 4.18.0.0
instance Foldable1 Complex where
foldMap1 f (x :+ y) = f x <> f y
toNonEmpty (x :+ y) = x :| y : []
-------------------------------------------------------------------------------
-- Instances for tuples
-------------------------------------------------------------------------------
-- 3+ tuples are not Foldable/Traversable
-- | @since 4.18.0.0
instance Foldable1 Solo where
foldMap1 f (MkSolo y) = f y
toNonEmpty (MkSolo x) = x :| []
minimum (MkSolo x) = x
maximum (MkSolo x) = x
head (MkSolo x) = x
last (MkSolo x) = x
-- | @since 4.18.0.0
instance Foldable1 ((,) a) where
foldMap1 f (_, y) = f y
toNonEmpty (_, x) = x :| []
minimum (_, x) = x
maximum (_, x) = x
head (_, x) = x
last (_, x) = x
-}
-------------------------------------------------------------------------------
-- Monoid / Semigroup instances
-------------------------------------------------------------------------------
{-
-- | @since 4.18.0.0
instance Foldable1 Dual where
foldMap1 = coerce
-- | @since 4.18.0.0
instance Foldable1 Sum where
foldMap1 = coerce
-- | @since 4.18.0.0
instance Foldable1 Product where
foldMap1 = coerce
-- | @since 4.18.0.0
instance Foldable1 Min where
foldMap1 = coerce
-- | @since 4.18.0.0
instance Foldable1 Max where
foldMap1 = coerce
-- | @since 4.18.0.0
instance Foldable1 First where
foldMap1 = coerce
-- | @since 4.18.0.0
instance Foldable1 Last where
foldMap1 = coerce
-- | @since 4.18.0.0
deriving instance (Foldable1 f) => Foldable1 (Mon.Alt f)
-- | @since 4.18.0.0
deriving instance (Foldable1 f) => Foldable1 (Mon.Ap f)
-}
-------------------------------------------------------------------------------
-- Extra instances
-------------------------------------------------------------------------------
{-
-- | @since 4.18.0.0
instance Foldable1 Identity where
foldMap1 = coerce
foldrMap1 g _ = coerce g
foldrMap1' g _ = coerce g
foldlMap1 g _ = coerce g
foldlMap1' g _ = coerce g
toNonEmpty (Identity x) = x :| []
last = coerce
head = coerce
minimum = coerce
maximum = coerce
-}
-- | It would be enough for either half of a product to be 'Foldable1'.
-- Other could be 'Foldable'.
instance (Foldable1 f, Foldable1 g) => Foldable1 (Functor.Product f g) where
foldMap1 f (Functor.Pair x y) = foldMap1 f x <> foldMap1 f y
foldrMap1 g f (Functor.Pair x y) = foldr f (foldrMap1 g f y) x
head (Functor.Pair x _) = head x
last (Functor.Pair _ y) = last y
-- | @since 4.18.0.0
instance (Foldable1 f, Foldable1 g) => Foldable1 (Functor.Sum f g) where
foldMap1 f (Functor.InL x) = foldMap1 f x
foldMap1 f (Functor.InR y) = foldMap1 f y
foldrMap1 g f (Functor.InL x) = foldrMap1 g f x
foldrMap1 g f (Functor.InR y) = foldrMap1 g f y
toNonEmpty (Functor.InL x) = toNonEmpty x
toNonEmpty (Functor.InR y) = toNonEmpty y
head (Functor.InL x) = head x
head (Functor.InR y) = head y
last (Functor.InL x) = last x
last (Functor.InR y) = last y
minimum (Functor.InL x) = minimum x
minimum (Functor.InR y) = minimum y
maximum (Functor.InL x) = maximum x
maximum (Functor.InR y) = maximum y
-- | @since 4.18.0.0
instance (Foldable1 f, Foldable1 g) => Foldable1 (Compose f g) where
foldMap1 f = foldMap1 (foldMap1 f) . getCompose
foldrMap1 f g = foldrMap1 (foldrMap1 f g) (\xs x -> foldr g x xs) . getCompose
head = head . head . getCompose
last = last . last . getCompose