packages feed

fresnel-0.0.0.0: src/Fresnel/Profunctor/Coexp.hs

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE RankNTypes #-}
module Fresnel.Profunctor.Coexp
( -- * Coexponential profunctor
  Coexp(..)
  -- * Construction
, coexp
  -- * Elimination
, recall
, forget
) where

import Data.Profunctor
import Data.Profunctor.Unsafe
import Data.Coerce

-- Coexponential

-- | Coexponentials are the dual of functions, consisting of an argument of type @a@ (derived within an environment of type @s@) and a continuation from the return type @b@ (extending to the eventual result type @t@). As such, they naturally have the shape of optics, relating the outer context @s -> t@ to the inner @a -> b@.
newtype Coexp s t b a = Coexp { withCoexp :: forall r . ((s -> a) -> (b -> t) -> r) -> r }

instance Functor (Coexp s t b) where
  fmap = rmap

instance Monoid t => Applicative (Coexp s t b) where
  pure a = coexp (pure a) mempty
  f <*> a = withCoexp f $ \ f kf -> withCoexp a $ \ a ka -> coexp (f <*> a) (mappend <$> kf <*> ka)

instance Profunctor (Coexp s t) where
  dimap f g c = withCoexp c $ \ recall forget -> coexp (g . recall) (forget . f)
  lmap f c = withCoexp c $ \ recall forget -> coexp recall (forget . f)
  rmap g c = withCoexp c $ \ recall forget -> coexp (g . recall) forget
  (#.) = const coerce
  (.#) = fmap coerce . const

instance Semigroup (Coexp a b b a) where
  c1 <> c2 = withCoexp c1 $ \ r1 f1 -> withCoexp c2 $ \ r2 f2 -> coexp (r2 . r1) (f1 . f2)

instance Monoid (Coexp a b b a) where
  mempty = coexp id id


-- Construction

coexp :: (s -> a) -> (b -> t) -> Coexp s t b a
coexp recall forget = Coexp (\ k -> k recall forget)


-- Elimination

recall :: Coexp s t b a -> (s -> a)
recall c = withCoexp c const

forget :: Coexp s t b a -> (b -> t)
forget c = withCoexp c (const id)