{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE CPP #-} -- | -- Module : Data.Accumulator -- Copyright : (c) Francesco Gazzetta 2017 -- License : BSD3 (see the file LICENSE) -- -- Maintainer : francygazz@gmail.org -- Stability : stable -- Portability : portable -- -- Accumulators, primarily useful for Polyvariadic module Data.Accumulator ( Accumulator (..) , singleton , accumulateMany , AccumulatorSemigroup (..) ) where import qualified Data.Set as Set import Data.Foldable (foldl') import Data.Semigroup #if !MIN_VERSION_base(4,8,0) import Data.Foldable #endif -- | An 'Accumulator c i' supports accumulation of elements of type i in it. -- This is different from 'Semigroup' or 'Monoid', where '<>' acts between -- two values with the same type. class Accumulator acc x where accumulate :: x -> acc -> acc -- ^ Accumulate a value -- | Accumulate a single value in a 'Monoid' singleton :: (Accumulator acc x, Monoid acc) => x -> acc singleton = flip accumulate mempty -- | Strictly accumulate multiple values from a 'Foldable', from left to right accumulateMany :: (Foldable f, Accumulator acc x) => f x -> acc -> acc accumulateMany xs acc = foldl' (flip accumulate) acc xs instance Accumulator [a] a where accumulate = (:) instance Ord a => Accumulator (Set.Set a) a where accumulate = Set.insert instance Semigroup m => Accumulator (AccumulatorSemigroup m) (AccumulatorSemigroup m) where accumulate = (<>) -- | Lift a 'Semigroup' into an Accumulator. This is a newtype because (<>) isn't always the ideal way of accumulating newtype AccumulatorSemigroup a = AccumulatorSemigroup {getAccumulatorSemigroup :: a} instance Semigroup m => Semigroup (AccumulatorSemigroup m) where (<>) (AccumulatorSemigroup a) (AccumulatorSemigroup b) = AccumulatorSemigroup $ (<>) a b