packages feed

linear-generics-0.1.0.0: tests/Generics/Deriving/Copoint.hs

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

{-# LANGUAGE DefaultSignatures #-}

{-# LANGUAGE PolyKinds #-}

{-# LANGUAGE Safe #-}
{-# LANGUAGE TypeApplications #-}

module Generics.Deriving.Copoint (
  -- * GCopoint class
    GCopoint(..)

  -- * Default method
  , gcopointdefault

  -- * Internal class
  , GCopoint'(..)

  ) where

import           Control.Applicative (WrappedMonad)

import           Data.Monoid (Dual)
import qualified Data.Monoid as Monoid (Sum)

import           Generics.Linear

import           Data.Ord (Down)

import           Data.Functor.Identity (Identity)
import           Data.Monoid (Alt)

import qualified Data.Functor.Sum as Functor (Sum)
import           Data.Semigroup (Arg, First, Last, Max, Min, WrappedMonoid)

--------------------------------------------------------------------------------
-- Generic copoint
--------------------------------------------------------------------------------

-- General copoint may return 'Nothing'

class GCopoint' t where
    gcopoint' :: (a -> b) -> t a -> Maybe b

instance GCopoint' V1 where
    gcopoint' _ _ = Nothing

instance GCopoint' U1 where
    gcopoint' _ U1 = Nothing

instance GCopoint' Par1 where
    gcopoint' f (Par1 a) = Just (f a)

instance GCopoint' (K1 i c) where
    gcopoint' _ _ = Nothing

instance GCopoint' f => GCopoint' (M1 i c f) where
    gcopoint' f (M1 a) = gcopoint' f a

instance GCopoint' f => GCopoint' (MP1 m f) where
    gcopoint' f (MP1 a) = gcopoint' f a

instance (GCopoint' f, GCopoint' g) => GCopoint' (f :+: g) where
    gcopoint' f (L1 a) = gcopoint' f a
    gcopoint' f (R1 a) = gcopoint' f a

-- Favours left "hole" for copoint
instance (GCopoint' f, GCopoint' g) => GCopoint' (f :*: g) where
    gcopoint' f (a :*: b) = case (gcopoint' f a) of
                             Just x -> Just x
                             Nothing -> gcopoint' f b

instance (GCopoint' f, GCopoint g) => GCopoint' (f :.: g) where
    gcopoint' f (Comp1 x) = gcopoint' (f . gcopoint) x

class GCopoint d where
  gcopoint :: d a -> a
  default gcopoint :: (Generic1 d, GCopoint' (Rep1 d))
                   => (d a -> a)
  gcopoint = gcopointdefault

gcopointdefault :: (Generic1 d, GCopoint' (Rep1 d))
                => d a -> a
gcopointdefault x = case (gcopoint' id . from1 $ x) of
                      Just x' -> x'
                      Nothing -> error "Data type is not copointed"

-- instance (Generic1 d, GCopoint' (Rep1 d)) => GCopoint d

-- Base types instances
instance GCopoint ((,) a)
instance GCopoint ((,,) a b)
instance GCopoint ((,,,) a b c)
instance GCopoint ((,,,,) a b c d)
instance GCopoint ((,,,,,) a b c d e)
instance GCopoint ((,,,,,,) a b c d e f)
instance GCopoint f => GCopoint (Alt f)
instance GCopoint (Arg a)
instance GCopoint Down
instance GCopoint Dual
instance GCopoint First
instance GCopoint Identity
instance GCopoint Last
instance GCopoint Max
instance GCopoint Min
instance (GCopoint f, GCopoint g) => GCopoint (Functor.Sum f g)
instance GCopoint Monoid.Sum
instance GCopoint m => GCopoint (WrappedMonad m)
instance GCopoint WrappedMonoid