{-# OPTIONS_GHC -Wno-redundant-constraints #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Relude.Extra.Foldable1
( Foldable1 (..)
, foldl1'
) where
import Relude hiding (Product (..), Sum (..))
import Relude.Extra.Newtype (( #. ))
import Data.Functor.Product (Product (..))
import Data.Functor.Sum (Sum (..))
import GHC.TypeLits (ErrorMessage (..), TypeError)
import qualified Data.Semigroup as SG
class Foldable f => Foldable1 f where
{-# MINIMAL foldMap1 #-}
foldMap1 :: Semigroup m => (a -> m) -> f a -> m
fold1 :: Semigroup m => f m -> m
fold1 = foldMap1 id
toNonEmpty :: f a -> NonEmpty a
toNonEmpty = foldMap1 (:|[])
head1 :: f a -> a
head1 = SG.getFirst #. foldMap1 SG.First
last1 :: f a -> a
last1 = SG.getLast #. foldMap1 SG.Last
maximum1 :: Ord a => f a -> a
maximum1 = SG.getMax #. foldMap1 SG.Max
minimum1 :: Ord a => f a -> a
minimum1 = SG.getMin #. foldMap1 SG.Min
instance Foldable1 NonEmpty where
fold1 :: Semigroup m => NonEmpty m -> m
fold1 = sconcat
{-# INLINE fold1 #-}
foldMap1 :: forall m a . Semigroup m => (a -> m) -> NonEmpty a -> m
foldMap1 f (a :| as) = foldr go f as a
where
go :: a -> (a -> m) -> a -> m
go b g x = f x <> g b
{-# INLINE foldMap1 #-}
toNonEmpty :: NonEmpty a -> NonEmpty a
toNonEmpty = id
{-# INLINE toNonEmpty #-}
head1, last1 :: NonEmpty a -> a
head1 = head
last1 = last
{-# INLINE head1 #-}
{-# INLINE last1 #-}
maximum1, minimum1 :: Ord a => NonEmpty a -> a
maximum1 = foldl1' max
minimum1 = foldl1' min
{-# INLINE maximum1 #-}
{-# INLINE minimum1 #-}
instance Foldable1 Identity where
foldMap1 :: Semigroup m => (a -> m) -> Identity a -> m
foldMap1 = coerce
{-# INLINE foldMap1 #-}
fold1 :: Semigroup m => Identity m -> m
fold1 = coerce
{-# INLINE fold1 #-}
toNonEmpty :: Identity a -> NonEmpty a
toNonEmpty = (:|[]) . coerce
{-# INLINE toNonEmpty #-}
head1 :: Identity a -> a
head1 = coerce
{-# INLINE head1 #-}
last1 :: Identity a -> a
last1 = coerce
{-# INLINE last1 #-}
maximum1 :: Ord a => Identity a -> a
maximum1 = coerce
{-# INLINE maximum1 #-}
minimum1 :: Ord a => Identity a -> a
minimum1 = coerce
{-# INLINE minimum1 #-}
instance Foldable1 ((,) c) where
foldMap1 :: Semigroup m => (a -> m) -> (c, a) -> m
foldMap1 f = f . snd
{-# INLINE foldMap1 #-}
fold1 :: Semigroup m => (c, m) -> m
fold1 = snd
{-# INLINE fold1 #-}
toNonEmpty :: (c, a) -> NonEmpty a
toNonEmpty (_, y) = (y :| [])
{-# INLINE toNonEmpty #-}
head1, last1 :: (c, a) -> a
head1 = snd
last1 = snd
{-# INLINE head1 #-}
{-# INLINE last1 #-}
maximum1, minimum1 :: Ord a => (c, a) -> a
maximum1 = snd
minimum1 = snd
{-# INLINE maximum1 #-}
{-# INLINE minimum1 #-}
instance (Foldable1 f, Foldable1 g) => Foldable1 (Compose f g) where
foldMap1 :: Semigroup m => (a -> m) -> Compose f g a -> m
foldMap1 f = foldMap1 (foldMap1 f) . getCompose
{-# INLINE foldMap1 #-}
head1 :: Compose f g a -> a
head1 = head1 . head1 . getCompose
{-# INLINE head1 #-}
last1 :: Compose f g a -> a
last1 = last1 . last1 . getCompose
{-# INLINE last1 #-}
instance (Foldable1 f, Foldable1 g) => Foldable1 (Product f g) where
foldMap1 :: Semigroup m => (a -> m) -> Product f g a -> m
foldMap1 f (Pair a b) = foldMap1 f a <> foldMap1 f b
{-# INLINE foldMap1 #-}
instance (Foldable1 f, Foldable1 g) => Foldable1 (Sum f g) where
foldMap1 :: Semigroup m => (a -> m) -> Sum f g a -> m
foldMap1 f (InL x) = foldMap1 f x
foldMap1 f (InR y) = foldMap1 f y
{-# INLINE foldMap1 #-}
type family IsListError :: Constraint
where
IsListError = TypeError
( 'Text "The methods of the 'Foldable1' type class work with non-empty containers."
':$$: 'Text "However, one of the 'Foldable1' functions is applied to the List."
':$$: 'Text ""
':$$: 'Text "Possible fixes:"
':$$: 'Text " * Replace []"
':$$: 'Text " with one of the: 'NonEmpty', 'Identity', '(c,)', 'Compose f g', 'Product f g', 'Sum f g'"
':$$: 'Text " * Or use 'Foldable' class for your own risk."
)
instance IsListError => Foldable1 [] where
foldMap1 :: Semigroup m => (a -> m) -> [a] -> m
foldMap1 _ _ = error "Unreachable list instance of Foldable1"
fold1 :: Semigroup m => [m] -> m
fold1 _ = error "Unreachable list instance of Foldable1"
toNonEmpty :: [a] -> NonEmpty a
toNonEmpty _ = error "Unreachable list instance of Foldable1"
head1 :: [a] -> a
head1 _ = error "Unreachable list instance of Foldable1"
last1 :: [a] -> a
last1 _ = error "Unreachable list instance of Foldable1"
maximum1 :: Ord a => [a] -> a
maximum1 _ = error "Unreachable list instance of Foldable1"
minimum1 :: Ord a => [a] -> a
minimum1 _ = error "Unreachable list instance of Foldable1"
foldl1' :: (a -> a -> a) -> NonEmpty a -> a
foldl1' _ (x :| []) = x
foldl1' f (x :| (y:ys)) = foldl' f (f x y) ys
{-# INLINE foldl1' #-}