{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes       #-}
{-# LANGUAGE TypeOperators    #-}
{-# LANGUAGE TypeFamilies     #-}

-----------------------------------------------------------------------------
-- |
-- Module      :  Generics.MultiRec.Eq
-- Copyright   :  (c) 2008 Universiteit Utrecht
-- License     :  BSD3
--
-- Maintainer  :  generics@haskell.org
-- Stability   :  experimental
-- Portability :  non-portable
--
-- Generic equality.
--
-----------------------------------------------------------------------------
module Generics.MultiRec.Eq where

import Generics.MultiRec.Base

-- * Generic equality

class HEq f where
  heq :: s ix ->
         (forall ix. Ix s ix => s ix -> r ix -> r ix -> Bool) ->
         f s r ix -> f s r ix -> Bool

instance HEq (I xi) where
  heq _ eq (I x1) (I x2) = eq index x1 x2

instance Eq x => HEq (K x) where
  heq _ eq (K x1) (K x2) = x1 == x2

instance (HEq f, HEq g) => HEq (f :+: g) where
  heq ix eq (L x1) (L x2) = heq ix eq x1 x2
  heq ix eq (R y1) (R y2) = heq ix eq y1 y2
  heq _  eq _     _       = False

instance (HEq f, HEq g) => HEq (f :*: g) where
  heq ix eq (x1 :*: y1) (x2 :*: y2) = heq ix eq x1 x2 && heq ix eq y1 y2

-- The following instance does not compile with ghc-6.8.2
instance HEq f => HEq (f :>: ix) where
  heq ix eq (Tag x1) (Tag x2) = heq ix eq x1 x2

eq :: (Ix s ix, HEq (PF s)) => s ix -> ix -> ix -> Bool
eq ix x1 x2 = heq ix (\ ix (I0 x1) (I0 x2) -> eq ix x1 x2) (from x1) (from x2)

-- Note:
-- 
-- We do not declare an equality instance such as
--
--   instance (Ix s ix, HEq (PF s)) => Eq ix where
--     (==) = eq index
--
-- because "s" is not mentioned on the right hand side.
-- One datatype may belong to multiple systems, and
-- although the generic equality instances should be
-- the same, there is no good way to decide which instance
-- to use.
--
-- For a concrete "s", it is still possible to manually
-- define an "Eq" instance as above.