{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE Safe #-}
module Data.Generics.Is.Internal (
   -- * Getting return types from functions
   Constructs
  ,construct

   -- | An instance of @Rebase' a b@ exists if and only if @Base a ~ b@.
   -- 
   --   By giving this information to the typechecker as a constraint, it can
   --   agressively unify type variables, thus avoiding ambiguity when
   --   working with polymorphic types.
   --
   --   >>> imports Data.Generics.Is.Generic
   --   
   --   >>> is Just Nothing
   --   False
   -- 
   --   >>> is (:) [1,2,3]
   --   True

  ,Base
  ,Rebase(..)

   -- | Defines 'Generic' equality on the head constructor.
  ,EqHead(..)

  ) where

import GHC.Generics

class Rebase c a where
  rebase :: c -> a
instance (Rebase c c) where
  rebase = id
instance (Rebase b c) => Rebase (a -> b) c where
  rebase c = rebase (c (error "Data.Generics.Is.Internal#rebase"))

type family Base b  where
  Base (a -> b) = Base b
  Base b        = b

type Constructs a b = (Rebase a b, Base a ~ b)
construct :: (Constructs a b) => a -> b
construct = rebase

class EqHead f where
  eqH :: f a -> f a -> Bool

instance EqHead V1 where
  eqH _ = const True

instance EqHead U1 where
  eqH _ = const True

instance EqHead (f :*: g) where
  eqH _ = const True

instance EqHead (K1 i c) where
  eqH _ = const True

instance (EqHead f) => EqHead (M1 i t f) where
  eqH (M1 x) = let r = eqH x in \(M1 y) -> r y

instance (EqHead f, EqHead g) => EqHead (f :+: g) where
  eqH (L1 x) = let r = eqH x in \case { (L1 y) -> r y; _ -> False }
  eqH (R1 x) = let r = eqH x in \case { (R1 y) -> r y; _ -> False }