{-# LANGUAGE GADTs         #-}
{-# LANGUAGE RankNTypes    #-}
{-# LANGUAGE TypeOperators #-}

module HFunctor where

import Control.Monad (liftM, liftM2)
import Control.Applicative (Applicative(..), liftA, liftA2, WrappedMonad(..))

import Base

-- * Generic map

-- Deviating from the paper, we define an 'hmapA' that works
-- on applicative functors. The original 'hmap' as given in the
-- paper is a special case.

class HFunctor f where
  hmapA :: (Applicative a) =>
           (forall ix. Ix s ix => s ix -> r ix -> a (r' ix)) ->
           f s r ix -> a (f s r' ix)

instance HFunctor (I xi) where
  hmapA f (I x) = liftA I (f index x)

instance HFunctor (K x) where
  hmapA _ (K x)  = pure (K x)

instance (HFunctor f, HFunctor g) => HFunctor (f :+: g) where
  hmapA f (L x) = liftA L (hmapA f x)
  hmapA f (R y) = liftA R (hmapA f y)

instance (HFunctor f, HFunctor g) => HFunctor (f :*: g) where
  hmapA f (x :*: y) = liftA2 (:*:) (hmapA f x) (hmapA f y)

instance HFunctor f => HFunctor (f :>: ix) where
  hmapA f (Tag x) = liftA Tag (hmapA f x)


hmap  :: (HFunctor f) =>
         (forall ix. Ix s ix => s ix -> r ix -> r' ix) ->
         f s r ix -> f s r' ix
hmap f x = unI0 (hmapA (\ ix x -> I0 (f ix x)) x)

hmapM :: (HFunctor f, Monad m) =>
         (forall ix. Ix s ix => s ix -> r ix -> m (r' ix)) ->
         f s r ix -> m (f s r' ix)
hmapM f x = unwrapMonad (hmapA (\ ix x -> WrapMonad (f ix x)) x)
