-----------------------------------------------------------------------------
-- |
-- Module      :  Generics.EMGM.Data.Either
-- Copyright   :  (c) 2008, 2009 Universiteit Utrecht
-- License     :  BSD3
--
-- Maintainer  :  generics@haskell.org
-- Stability   :  experimental
-- Portability :  non-portable
--
-- Summary: Generic representation and instances for 'Either'.
-----------------------------------------------------------------------------

{-# OPTIONS_GHC -Wall #-}

{-# LANGUAGE TypeOperators          #-}
{-# LANGUAGE TypeSynonymInstances   #-}
{-# LANGUAGE FlexibleInstances      #-}
{-# LANGUAGE FlexibleContexts       #-}
{-# LANGUAGE MultiParamTypeClasses  #-}
{-# LANGUAGE OverlappingInstances   #-}
{-# OPTIONS -fno-warn-orphans       #-}
{-  OPTIONS -ddump-splices           -}

module Generics.EMGM.Data.Either (
  EitherS,
  conLeft,
  conRight,
  repEither,
  frepEither,
  frep2Either,
  frep3Either,
  bifrep2Either,
) where

import Control.Applicative (Alternative, pure)

import Generics.EMGM.Base
import Generics.EMGM.Functions.Collect
import Generics.EMGM.Functions.Everywhere
import Generics.EMGM.Functions.Meta

-----------------------------------------------------------------------------
-- Embedding-projection pair
-----------------------------------------------------------------------------

-- Structure representation type for 'Either'.
type EitherS a b = a :+: b

epEither :: EP (Either a b) (EitherS a b)
epEither = EP fromEither toEither
  where
    fromEither (Left a)  = L a
    fromEither (Right b) = R b
    toEither (L a) = Left a
    toEither (R b) = Right b

instance HasEP (Either a b) (EitherS a b) where
  epOf _ = epEither

-----------------------------------------------------------------------------
-- Representation values
-----------------------------------------------------------------------------

-- | Constructor description for 'Left'.
conLeft :: ConDescr
conLeft = ConDescr "Left" 1 False Prefix

-- | Constructor description for 'Right'.
conRight :: ConDescr
conRight = ConDescr "Right" 1 False Prefix

-- | Representation of 'Either' for 'frep'.
frepEither :: (Generic g) => g a -> g b -> g (Either a b)
frepEither ra rb =
  rtype
    epEither
    (rcon conLeft ra `rsum` rcon conRight rb)

-- | Representation of 'Either' for 'rep'.
repEither :: (Generic g, Rep g a, Rep g b) => g (Either a b)
repEither =
  rtype
    epEither
    (rcon conLeft rep `rsum` rcon conRight rep)

-- | Representation of 'Either' for 'frep2'.
frep2Either :: (Generic2 g) => g a1 a2 -> g b1 b2 -> g (Either a1 b1) (Either a2 b2)
frep2Either ra rb =
  rtype2
    epEither epEither
    (rcon2 conLeft ra `rsum2` rcon2 conRight rb)

-- | Representation of 'Either' for 'bifrep2'.
bifrep2Either :: (Generic2 g) => g a1 a2 -> g b1 b2 -> g (Either a1 b1) (Either a2 b2)
bifrep2Either =
  frep2Either

-- | Representation of 'Either' for 'frep3'.
frep3Either :: (Generic3 g) => g a1 a2 a3 -> g b1 b2 b3 -> g (Either a1 b1) (Either a2 b2) (Either a3 b3)
frep3Either ra rb =
  rtype3
    epEither epEither epEither
    (rcon3 conLeft ra `rsum3` rcon3 conRight rb)

-----------------------------------------------------------------------------
-- Instance declarations
-----------------------------------------------------------------------------

instance (Generic g, Rep g a, Rep g b) => Rep g (Either a b) where
  rep = repEither

instance (Generic2 g) => BiFRep2 g Either where
  bifrep2 = bifrep2Either

instance (Alternative f) => Rep (Collect f (Either a b)) (Either a b) where
  rep = Collect pure

instance (Rep (Everywhere (Either a b)) a, Rep (Everywhere (Either a b)) b)
         => Rep (Everywhere (Either a b)) (Either a b) where
  rep = Everywhere app
    where
      app f x =
        case x of
          Left a  -> f (Left (selEverywhere rep f a))
          Right b -> f (Right (selEverywhere rep f b))

instance Rep (Everywhere' (Either a b)) (Either a b) where
  rep = Everywhere' ($)