{-# LANGUAGE TypeOperators            #-}
{-# LANGUAGE FlexibleContexts         #-}
{-# LANGUAGE FlexibleInstances        #-}

-----------------------------------------------------------------------------
-- |
-- Module      :  Generics.Regular.Functions.Seq
-- Copyright   :  (c) 2009 Universiteit Utrecht
-- License     :  BSD3
--
-- Maintainer  :  generics@haskell.org
-- Stability   :  experimental
-- Portability :  non-portable
--
-- Summary: Deep generic seq. Used to fully evaluate a term.
-----------------------------------------------------------------------------

module Generics.Regular.Functions.Seq (

    DeepSeq (..), Seq(..), gdseq
    
  ) where

import Data.List
import Generics.Regular.Base

-- | The class for generic deep seq.
class Seq f where
  gseq :: (a -> b -> b) -> f a -> b -> b

instance Seq I where
  gseq f (I x) = f x

-- | For constants we rely on the |DeepSeq| class.
instance (DeepSeq a) => Seq (K a) where
  gseq _ (K x) = dseq x
  
instance Seq U where
  gseq _ U = id

instance (Seq f, Seq g) => Seq (f :+: g) where
  gseq f (L x) = gseq f x
  gseq f (R y) = gseq f y

instance (Seq f, Seq g) => Seq (f :*: g) where
  gseq f (x :*: y) = gseq f x . gseq f y

instance Seq f => Seq (C c f) where
  gseq f (C x) = gseq f x

instance Seq f => Seq (S s f) where
  gseq f (S x) = gseq f x

-- | Deep, generic version of seq.

gdseq :: (Regular a, Seq (PF a)) => a -> b -> b
gdseq p = gseq gdseq (from p)

-- | A general class for expressing deep seq. It is used in the 'K' case for
-- the generic seq.
--
-- We do not give an instance of the form
-- @instance (Regular a, Seq (PF a)) => DeepSeq a where dseq = gdseq@
-- because this requires undecidable instances. However, any type for which
-- there is a generic instance can be given a trivial instance of 'DeepSeq' by
-- using 'gdseq'.
class DeepSeq a where
  dseq   :: a -> b -> b
  dseq = seq

instance DeepSeq Int
instance DeepSeq Integer
instance DeepSeq Char
instance DeepSeq Float
instance DeepSeq Double
instance DeepSeq ()

instance DeepSeq a => DeepSeq [a] where
  dseq xs b = foldl' (flip dseq) b xs

instance DeepSeq a => DeepSeq (Maybe a) where
  dseq Nothing  b = b
  dseq (Just a) b = dseq a b

instance (DeepSeq a, DeepSeq b) => DeepSeq (Either a b) where
  dseq (Left  x) b = dseq x b
  dseq (Right x) b = dseq x b