{- | Geriving instances, generically.
   This module shares some of the underlying ideas and implementations of the
   [generic-data](https://hackage.haskell.org/package/generic-data-0.8.1.0/docs/Generic-Data.html)
   package, allowing us to derive a bunch of instances using the underlying 'Generic' implementation,
   but in a more declarative way.

   In particular we introduc the 'Generically' newtype wrapper to be used with '-XDerivingVia' to make
   derivation explicit. For example:

@
  data Foo = Foo
       deriving Generic
       deriving Eq via Generically Foo
@

-}

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE UndecidableInstances #-}

module Language.Haskell.Liquid.Types.Generics where

import GHC.Generics
import Data.Hashable
import Data.Binary
import Data.Hashable.Generic
import Data.Function

newtype Generically a = Generically a deriving forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall a x. Rep (Generically a) x -> Generically a
forall a x. Generically a -> Rep (Generically a) x
$cto :: forall a x. Rep (Generically a) x -> Generically a
$cfrom :: forall a x. Generically a -> Rep (Generically a) x
Generic

-- * 'Hashable'

instance (Eq (Generically a), Generic a, GHashable Zero (Rep a)) => Hashable (Generically a) where
  hashWithSalt :: Int -> Generically a -> Int
hashWithSalt Int
s (Generically a
a) = forall a. (Generic a, GHashable Zero (Rep a)) => Int -> a -> Int
genericHashWithSalt Int
s a
a

-- * 'Binary'

instance (Generic a, GBinaryPut (Rep a), GBinaryGet (Rep a)) => Binary (Generically a) where
  get :: Get (Generically a)
get = forall a. a -> Generically a
Generically forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Generic a => Rep a () -> a
to' forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall {k} (f :: k -> *) (t :: k). GBinaryGet f => Get (f t)
gget
  put :: Generically a -> Put
put (Generically a
a) = forall {k} (f :: k -> *) (t :: k). GBinaryPut f => f t -> Put
gput (forall a. Generic a => a -> Rep a ()
from' a
a)

-- * 'Eq'

-- | Generic @('==')@.
--
-- @
-- instance 'Eq' MyType where
--   ('==') = 'geq'
-- @
geq :: (Generic a, Eq (Rep a ())) => a -> a -> Bool
geq :: forall a. (Generic a, Eq (Rep a ())) => a -> a -> Bool
geq = forall a. Eq a => a -> a -> Bool
(==) forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` forall a. Generic a => a -> Rep a ()
from'

instance (Generic a, Eq (Rep a ())) => Eq (Generically a) where
  (Generically a
a) == :: Generically a -> Generically a -> Bool
== (Generically a
b) = forall a. (Generic a, Eq (Rep a ())) => a -> a -> Bool
geq a
a a
b

-- | A helper for better type inference.
from' :: Generic a => a -> Rep a ()
from' :: forall a. Generic a => a -> Rep a ()
from' = forall a x. Generic a => a -> Rep a x
from

to' :: Generic a => Rep a () -> a
to' :: forall a. Generic a => Rep a () -> a
to' = forall a x. Generic a => Rep a x -> a
to