module Data.Naperian
( module Data.Functor1
, Naperian(..)
, nindex
, Distribute1
, distributeTabulate
, distributeRepresentable
, distributeIso
, distributeCoerce
, fmapCotraverse1
, zipWithNap
, apNap
, pureNap
, bindNap
, distributeNap
, collectNap
, Logarithm(..)
, tabulateLog
, indexLog
) where
import Control.Applicative
import Control.Comonad.Cofree
import Control.Comonad.Trans.Traced
import Control.Monad.Trans.Identity
import Control.Monad.Trans.Reader
import Data.Distributive
import Data.Functor.Compose
import Data.Functor.Identity
import Data.Functor.Product
import Data.Functor.Rep
import Data.Functor1
import Data.Functor1.Applied
import Data.Stream.Infinite
import Data.Type.Coercion
import GHC.Generics hiding (Rep)
class Distributive f => Naperian f where
distribute1 :: Functor1 w => w f -> f (w Identity)
default distribute1 ::
(Generic1 f, Naperian (Rep1 f), Functor1 w)
=> w f -> f (w Identity)
distribute1 = distributeIso (from1 :: f a -> Rep1 f a) to1
cotraverse1 :: Functor1 w => (w Identity -> a) -> w f -> f a
cotraverse1 f = fmap f . distribute1
collect1 :: Functor1 w => (forall x. g x -> f x) -> w g -> f (w Identity)
collect1 f = distribute1 . map1 f
twiddle1 ::
Functor1 w => (w Identity -> a) -> (forall x. g x -> f x) -> w g -> f a
twiddle1 f g = fmap f . distribute1 . map1 g
ntabulate :: ((forall x. f x -> x) -> a) -> f a
ntabulate f = cotraverse1 (\(TabulateArg g) -> g runIdentity) (TabulateArg f)
newtype TabulateArg a f = TabulateArg ((forall x. f x -> x) -> a)
instance Functor1 (TabulateArg a) where
map1 f (TabulateArg g) = TabulateArg $ \h -> g (h . f)
nindex :: f a -> (forall x. f x -> x) -> a
nindex x f = f x
type Distribute1 f = forall w. Functor1 w => w f -> f (w Identity)
distributeTabulate :: Naperian f => Distribute1 f
distributeTabulate w = ntabulate $ \f -> map1 (Identity . f) w
distributeRepresentable :: Representable f => Distribute1 f
distributeRepresentable w = tabulate $ \f -> map1 (Identity . (`index` f)) w
distributeIso ::
Naperian g
=> (forall x. f x -> g x)
-> (forall x. g x -> f x)
-> Distribute1 f
distributeIso t frm = frm . distribute1 . map1 t
distributeCoerce ::
forall g f. Naperian g
=> (forall x. Coercion (g x) (f x))
-> Distribute1 f
distributeCoerce x = coerceWith x . distribute1 . mapCoerce1 (sym x)
fmapCotraverse1 :: Naperian f => (a -> b) -> f a -> f b
fmapCotraverse1 f = cotraverse1 (f . runIdentity . runApplied) . Applied
data PairOf a b f = PairOf (f a) (f b)
instance Functor1 (PairOf a b) where
map1 f (PairOf x y) = PairOf (f x) (f y)
zipWithNap :: Naperian f => (a -> b -> c) -> f a -> f b -> f c
zipWithNap f as bs =
cotraverse1 (\(PairOf (Identity a) (Identity b)) -> f a b) (PairOf as bs)
apNap :: Naperian f => f (a -> b) -> f a -> f b
apNap = zipWithNap ($)
newtype Const1 a (f :: * -> *) = Const1 { runConst1 :: a }
instance Functor1 (Const1 a) where
map1 _ (Const1 x) = Const1 x
pureNap :: Naperian f => a -> f a
pureNap = cotraverse1 runConst1 . Const1
data BindArgs a b f = BindArgs (f a) (a -> f b)
instance Functor1 (BindArgs a b) where
map1 f (BindArgs x g) = BindArgs (f x) (f . g)
bindNap :: Naperian f => f a -> (a -> f b) -> f b
bindNap as f =
cotraverse1 (\(BindArgs (Identity a) g) -> runIdentity (g a)) (BindArgs as f)
newtype Composed g a f = Composed { runComposed :: g (f a) }
instance Functor g => Functor1 (Composed g a) where
map1 f = Composed . fmap f . runComposed
distributeNap :: (Naperian f, Functor w) => w (f a) -> f (w a)
distributeNap = cotraverse1 (fmap runIdentity . runComposed) . Composed
collectNap :: (Naperian f, Functor w) => (a -> f b) -> w a -> f (w b)
collectNap f = distributeNap . fmap f
newtype Logarithm f = Logarithm { runLogarithm :: forall x. f x -> x }
tabulateLog :: Naperian f => (Logarithm f -> a) -> f a
tabulateLog f = ntabulate $ \x -> f (Logarithm x)
indexLog :: f a -> Logarithm f -> a
indexLog x (Logarithm f) = f x
instance Naperian Identity where
distribute1 = Identity
instance Naperian ((->) e) where
distribute1 w e = map1 (Identity . ($ e)) w
instance (Naperian f, Naperian g) => Naperian (Product f g) where
distribute1 =
Pair <$> collect1 (\(Pair x _) -> x) <*> collect1 (\(Pair _ y) -> y)
newtype AppCompose w g f = AppCompose { runAppCompose :: w (Compose f g) }
instance Functor1 w => Functor1 (AppCompose w g) where
map1 f = AppCompose . map1 (Compose . f . getCompose) . runAppCompose
instance (Naperian f, Naperian g) => Naperian (Compose f g) where
distribute1 =
Compose .
cotraverse1 (collect1 (runIdentity . getCompose) . runAppCompose) .
AppCompose
instance Naperian f => Naperian (IdentityT f) where
distribute1 = distributeCoerce (Coercion :: Coercion (f x) (IdentityT f x))
instance Naperian f => Naperian (ReaderT e f) where
distribute1 =
distributeCoerce
(Coercion :: Coercion (Compose ((->) e) f x) (ReaderT e f x))
instance Naperian w => Naperian (TracedT s w) where
distribute1 =
distributeCoerce
(Coercion :: Coercion (Compose w ((->) s) x) (TracedT s w x))
instance Naperian f => Naperian (Cofree f) where
distribute1 =
distributeIso
(\(x :< xs) -> Pair (Identity x) (Compose xs))
(\(Pair (Identity x) (Compose xs)) -> x :< xs)
instance Naperian Stream where
distribute1 =
distributeIso
(\(x :> xs) -> Pair (Identity x) xs)
(\(Pair (Identity x) xs) -> x :> xs)
#if MIN_VERSION_distributive(0,5,1)
instance Naperian U1 where
distribute1 _ = U1
instance (Naperian f, Naperian g) => Naperian (f :*: g) where
distribute1 = distributeIso (\(x :*: y) -> Pair x y) (\(Pair x y) -> x :*: y)
instance (Naperian f, Naperian g) => Naperian (f :.: g) where
distribute1 =
distributeCoerce (Coercion :: Coercion (Compose f g x) ((:.:) f g x))
instance Naperian Par1 where
distribute1 = distributeCoerce (Coercion :: Coercion (Identity x) (Par1 x))
instance Naperian f => Naperian (Rec1 f) where
distribute1 = distributeCoerce (Coercion :: Coercion (f x) (Rec1 f x))
instance Naperian f => Naperian (M1 i c f) where
distribute1 = distributeCoerce (Coercion :: Coercion (f x) (M1 i c f x))
#endif