module Util.Universe where

import Prelude hiding ((.), id)
import Control.Arrow
import Control.Category
import Control.Monad
import Data.Functor.Classes
import Data.Functor.Compose
import qualified Data.List as List
import Data.Maybe
import Data.Universe.Class
import Data.Universe.Instances.Base ()
import Numeric.Natural

newtype Fn a b = Fn { unFn :: a -> b }
  deriving (Semigroup, Monoid, Universe, Finite, Functor, Applicative, Monad,
            Category, Arrow, ArrowApply, ArrowChoice, ArrowLoop)
instance (Universe a, Eq b) => Eq (Fn a b) where (==) = eq1
instance Universe a => Foldable (Fn a) where
    foldMap f (Fn φ) = foldMap (f . φ) universe
instance (Eq a, Finite a) => Traversable (Fn a) where
    sequenceA (Fn φ) =
        Fn . (\ bs a -> fromJust $ List.lookup a bs) . getCompose <$>
        traverse φ (Compose $ join (,) <$> universe)
instance Universe a => Eq1 (Fn a) where
    liftEq eq (Fn φ) (Fn χ) = liftEq eq (φ <$> universe) (χ <$> universe)

universalIndex :: (Eq a, Universe a) => a -> Natural
universalIndex a = List.genericLength $ List.takeWhile (/= a) universe