{-# LANGUAGE TypeOperators     #-}

-----------------------------------------------------------------------------
-- |
-- Module      :  Generics.Regular.Functions.Crush
-- Copyright   :  (c) 2008 Universiteit Utrecht
-- License     :  BSD3
--
-- Maintainer  :  generics@haskell.org
-- Stability   :  experimental
-- Portability :  non-portable
--
-- Summary: Generic crush.
-----------------------------------------------------------------------------

module Generics.Regular.Functions.Crush (

  -- * Crush functions
  Crush (..), Assoc(..),
  flattenl, flattenr, crushr, crushl

) where

import Generics.Regular.Base


-----------------------------------------------------------------------------
-- Crush functions.
-----------------------------------------------------------------------------

-- | Associativity of the binary operator used for 'crush'
data Assoc = AssocLeft  -- ^ Left-associative
           | AssocRight -- ^ Right-associative


-- | The @Crush@ class defines a right-associative crush on functorial values.
class Crush f where
  crush :: Assoc -> (a -> b -> b) -> b -> f a -> b

instance Crush I where
  crush _ op e (I x) = x `op` e

instance Crush (K a) where
  crush _ _ e _ = e

instance Crush U where
  crush _ _ e _ = e

instance (Crush f, Crush g) => Crush (f :+: g) where
  crush asc op e (L x) = crush asc op e x
  crush asc op e (R y) = crush asc op e y

instance (Crush f, Crush g) => Crush (f :*: g) where
  crush asc@AssocRight op e (x :*: y) = crush asc op (crush asc op e y) x
  crush asc@AssocLeft  op e (x :*: y) = crush asc op (crush asc op e x) y

instance Crush f => Crush (C c f) where
  crush asc op e (C x) = crush asc op e x

instance Crush f => Crush (S s f) where
  crush asc op e (S x) = crush asc op e x

-- | Flatten a structure by collecting all the elements present.
flattenr, flattenl :: Crush f => f a -> [a]
flattenr = crushr (:) []
flattenl = crushl (:) []

crushr, crushl :: Crush f => (a -> b -> b) -> b -> f a -> b
crushr = crush AssocRight
crushl = crush AssocLeft