module Data.Monoid.Reducer where

import Control.Category
import Data.Monoid

class Monoid o => Reducer a o where
    conv :: a -> o
    conv = snoc unit

    snoc :: o -> a -> o
    snoc o = op o . conv

    cons :: a -> o -> o
    cons = op . conv

repeat :: Reducer a o => a -> o
repeat x = let xs = cons x xs in xs

class Generator g where
    type Elem g

    mapReduce :: Reducer a o => (Elem g -> a) -> g -> o
    mapReduce f = mapTo f unit

    mapTo     :: Reducer a o => (Elem g -> a) -> o -> g -> o
    mapTo f o = op o . mapReduce f

    mapFrom   :: Reducer a o => (Elem g -> a) -> g -> o -> o
    mapFrom f = op . mapReduce f

reduce :: (Generator g, Reducer (Elem g) o) => g -> o
reduce = mapReduce id

mapReduceWith :: (Generator g, Reducer a o) => (o -> b) -> (Elem g -> a) -> g -> b
mapReduceWith f = result f . mapReduce

reduceWith :: (Generator g, Reducer (Elem g) o) => (o -> a) -> g -> a
reduceWith f = f . reduce
