proarrow-0.1.0.0: src/Proarrow/Category/Instance/Linear.hs
{-# LANGUAGE LinearTypes #-}
{- HLINT ignore "Avoid lambda using `infix`" -}
{- HLINT ignore "Use curry" -}
{- HLINT ignore "Use bimap" -}
{- HLINT ignore "Use tuple-section" -}
-- | The category of Haskell types and __linear functions__: the kind 'LINEAR' wraps 'Data.Kind.Type'
-- in 'L', and a morphism is a @a %1 -> b@ function. Symmetric monoidal closed with
-- @'L' a '**' 'L' b = 'L' (a, b)@. The categorical product is 'With', and only comonoid objects
-- (such as @'L' ('Ur' a)@) can be copied or discarded, so it is deliberately not
-- 'Proarrow.Category.Monoidal.CopyDiscard.CopyDiscard'.
module Proarrow.Category.Instance.Linear where
import Data.IORef (newIORef, readIORef, writeIORef)
import Data.Kind (Type)
import Data.Void (Void)
import System.IO.Unsafe (unsafeDupablePerformIO)
import Unsafe.Coerce (unsafeCoerce)
import Prelude (Bool (..), Either (..), Eq (..), Show (..), error, showParen, showString, (&&), (>))
import Proarrow.Category.Monoidal (Monoidal (..), MonoidalProfunctor (..), SymMonoidal (..))
import Proarrow.Category.Monoidal.Action (CoprodAction)
import Proarrow.Category.Monoidal.Closed (Closed (..))
import Proarrow.Category.Monoidal.Distributive (Distributive (..))
import Proarrow.Category.Monoidal.StarAutonomous (StarAutonomous (..))
import Proarrow.Category.Monoidal.Strength (Costrong (..))
import Proarrow.Colimit.BinaryCoproduct (Coprod (..), HasBinaryCoproducts (..))
import Proarrow.Colimit.Copower (Copowered (..))
import Proarrow.Colimit.Initial (HasInitialObject (..))
import Proarrow.Core (CAT, CategoryOf (..), Is, Profunctor (..), Promonad (..), UN, dimapDefault, type (+->))
import Proarrow.Functor (Functor (..), FunctorForRep (..))
import Proarrow.Limit.BinaryProduct (HasBinaryProducts (..))
import Proarrow.Limit.Power (Powered (..))
import Proarrow.Limit.Terminal (HasTerminalObject (..))
import Proarrow.Monoid (Comonoid (..))
import Proarrow.Profunctor.Corepresentable (Corep (..), Corepresentable (..))
import Proarrow.Profunctor.Instance.Composition ((:.:) (..))
import Proarrow.Profunctor.Representable (Rep (..))
type data LINEAR = L Type
type Linear :: CAT LINEAR
data Linear a b where
Linear :: (a %1 -> b) %1 -> Linear (L a) (L b)
unLinear :: (L a ~> L b) %1 -> (a %1 -> b)
unLinear (Linear f) = f
instance Profunctor Linear where
dimap = dimapDefault
r \\ Linear{} = r
instance Promonad Linear where
id = Linear \x -> x
Linear f . Linear g = Linear \x -> f (g x)
-- | Category of linear functions.
instance CategoryOf LINEAR where
type (~>) = Linear
type Ob (a :: LINEAR) = Is L a
instance MonoidalProfunctor Linear where
one = id
Linear f ** Linear g = Linear \(x, y) -> (f x, g y)
-- | Tuples as monoidal tensor. Tuples are not the binary product in LINEAR.
instance Monoidal LINEAR where
type Unit = L ()
type L a ** L b = L (a, b)
withOb2 r = r
leftUnitor = Linear \((), x) -> x
leftUnitorInv = Linear \x -> ((), x)
rightUnitor = Linear \(x, ()) -> x
rightUnitorInv = Linear \x -> (x, ())
associator = Linear \((x, y), z) -> (x, (y, z))
associatorInv = Linear \(x, (y, z)) -> ((x, y), z)
instance SymMonoidal LINEAR where
swap = Linear \(x, y) -> (y, x)
instance Closed LINEAR where
type a ~~> b = L (UN L a %1 -> UN L b)
withObExp r = r
curry (Linear f) = Linear \a b -> f (a, b)
apply = Linear \(f, a) -> f a
Linear f ^^^ Linear g = Linear \h x -> f (h (g x))
data family Forget :: LINEAR +-> Type
instance FunctorForRep Forget where
type Forget @ a = UN L a
fmap (Linear f) x = f x
-- | By creating the left adjoint to the forgetful functor,
-- we obtain the free-forgetful adjunction between Hask and LINEAR
instance Corepresentable (Rep Forget :: LINEAR +-> Type) where
type Rep Forget %% a = L (Ur a)
coindex (Rep f) = Linear \(Ur a) -> f a
cotabulate (Linear f) = Rep \a -> f (Ur a)
corepMap f = Linear \(Ur a) -> Ur (f a)
-- | Forget is a lax monoidal functor
instance MonoidalProfunctor (Rep Forget) where
one = Rep \() -> ()
Rep f ** Rep g = Rep \(x, y) -> (f x, g y)
-- | Forget is also a colax monoidal functor
instance MonoidalProfunctor (Corep Forget) where
one = Corep id
Corep f ** Corep g = Corep \(x, y) -> (f x, g y)
data Ur a where
Ur :: a -> Ur a
counitUr :: Ur a %1 -> a
counitUr (Ur a) = a
dupUr :: Ur a %1 -> Ur (Ur a)
dupUr (Ur a) = Ur (Ur a)
instance Functor Ur where
map f (Ur a) = Ur (f a)
instance Comonoid (L (Ur a)) where
counit = Linear \(Ur _) -> ()
comult = Linear \(Ur a) -> (Ur a, Ur a)
-- | @L Bool@ is a comonoid: a @Bool@ is duplicated and discarded by case analysis, which is
-- linear (it consumes the input exactly once). The same holds for any finite, pattern-matchable
-- classical type. Only the @Bool@ instance is spelled out here.
instance Comonoid (L Bool) where
counit = Linear \case True -> (); False -> ()
comult = Linear \case True -> (True, True); False -> (False, False)
instance HasBinaryCoproducts LINEAR where
type L a || L b = L (Either a b)
withObCoprod r = r
lft = Linear Left
rgt = Linear Right
Linear f ||| Linear g = Linear \case
Left x -> f x
Right y -> g y
instance HasInitialObject LINEAR where
type InitialObject = L Void
initiate = Linear \case {}
instance Costrong CoprodAction Linear where
coact (Linear uxuy) = loop . Linear Right
where
loop = Linear \ux -> case uxuy ux of Left x -> unLinear loop (Left x); Right b -> b
data Top where
Top :: a %1 -> Top
instance Show Top where
showsPrec _ _ = showString "⊤"
instance Eq Top where
_ == _ = True
data With a b where
With :: x %1 -> (x %1 -> a) -> (x %1 -> b) -> With a b
instance (Show a, Show b) => Show (With a b) where
showsPrec d (With x f g) = showParen (d > 9) (showString "mkWith " . showsPrec 10 (f x) . showString " " . showsPrec 10 (g x))
instance (Eq a, Eq b) => Eq (With a b) where
With x fa fb == With y ga gb = (fa x == ga y) && (fb x == gb y)
urWith :: Ur (With a b) %1 -> (Ur a, Ur b)
urWith (Ur (With x f g)) = (Ur (f x), Ur (g x))
mkWith :: a -> b -> With a b
mkWith a b = With () (\() -> a) (\() -> b)
instance HasTerminalObject LINEAR where
type TerminalObject = L Top
terminate = Linear Top
instance HasBinaryProducts LINEAR where
type L a && L b = L (With a b)
withObProd r = r
fst = Linear \(With x xa _) -> xa x
snd = Linear \(With x _ xb) -> xb x
Linear f &&& Linear g = Linear \x -> With x f g
instance Powered Type LINEAR where
type L a ^ n = L (n -> a)
withObPower r = r
power f = Linear \x n -> unLinear (f n) x
unpower (Linear f) n = Linear \x -> f x n
instance Copowered Type LINEAR where
type n *. L a = L (Ur n, a)
withObCopower r = r
copower f = Linear \(Ur n, a) -> unLinear (f n) a
uncopower (Linear f) n = Linear \x -> f (Ur n, x)
instance MonoidalProfunctor (Coprod Linear) where
one = Coprod (Linear \x -> x)
Coprod f ** Coprod g = Coprod (f +++ g)
instance Distributive LINEAR where
distL = Linear \(a, ebc) -> case ebc of Left b -> Left (a, b); Right c -> Right (a, c)
distR = Linear \(eab, c) -> case eab of Left a -> Left (a, c); Right b -> Right (b, c)
absorbL = Linear \(_a, v) -> case v of {}
absorbR = Linear \(v, _a) -> case v of {}
type Not a = a %1 -> ()
not :: (Not b %1 -> Not a) %1 -> a %1 -> b
not nbna a = dn \nb -> nbna nb a
not' :: (a %1 -> b) %1 -> Not b %1 -> Not a
not' ab nb a = nb (ab a)
newtype Par a b = Par (Not (Not a, Not b))
mkPar :: a %1 -> b %1 -> Par a b
mkPar a b = Par \(na, nb) -> case (na a, nb b) of ((), ()) -> ()
pairFst :: (a, b `Par` c) %1 -> (a, b) `Par` c
pairFst (a, Par f) = Par \(nab, nc) -> f (\b -> nab (a, b), nc)
pairSnd :: (a `Par` b, c) %1 -> a `Par` (b, c)
pairSnd (Par f, c) = Par \(na, nbc) -> f (na, \b -> nbc (b, c))
parAppL :: (a `Par` b) %1 -> Not a %1 -> b
parAppL (Par f) na = dn \nb -> f (na, nb)
parAppR :: (a `Par` b) %1 -> Not b %1 -> a
parAppR (Par f) nb = dn \na -> f (na, nb)
newtype Quest a = Quest (Not (Ur (Not a)))
notQuest :: Not (Quest a) %1 -> Ur (Not a)
notQuest nqa = dn \nuna -> nqa (Quest nuna)
unitQuest :: a %1 -> Quest a
unitQuest a = Quest \(Ur na) -> na a
multQuest :: Quest (Quest a) %1 -> Quest a
multQuest (Quest f) = Quest \(Ur na) -> f (Ur (\(Quest nuna) -> nuna (Ur na)))
questPar :: Par (Quest a) (Quest b) %1 -> Quest (Either a b)
questPar (Par f) = Quest (\(Ur g) -> f (\(Quest nuna) -> nuna (Ur (\a -> g (Left a))), \(Quest nunb) -> nunb (Ur (\b -> g (Right b)))))
-- LINEAR is not CompactClosed. And hence it is also not traced,
-- since any star autonomous category with a trace is compact closed.
instance StarAutonomous LINEAR where
type Dual (L a) = L (Not a)
withObDual r = r
dual (Linear f) = Linear (\nb a -> nb (f a))
dualInv (Linear f) = Linear (\b -> dn (\na -> f na b))
linDist (Linear f) = Linear (\a (b, c) -> f (a, b) c)
linDistInv (Linear f) = Linear (\(a, b) c -> f a (b, c))
doubleNeg = Linear dn
doubleNegInv = Linear (\a na -> na a)
-- | Double negation is possible with linear functions, though using `unsafeDupablePerformIO`.
-- Derived from https://gist.github.com/ant-arctica/7563282c57d9d1ce0c4520c543187932
-- TODO: only tested in GHCi, might get ruined by optimizations
dn :: Not (Not a) %1 -> a
dn nna =
let ref = unsafeDupablePerformIO (newIORef (error "Linear.dn: write failed"))
in case nna (unsafeLinear (fill ref)) of () -> unsafeDupablePerformIO (readIORef ref)
where
fill ref x = unsafeDupablePerformIO (writeIORef ref x)
unsafeLinear :: (a -> b) -> (a %1 -> b)
unsafeLinear = unsafeCoerce
unit :: L () ~> L (Par a (Not a))
unit = Linear \() -> Par \(na, nna) -> nna na
counit :: L (Not a, a) ~> L ()
counit = Linear \(na, a) -> na a
type p !~> q = forall a b. p a b %1 -> q a b
type NegComp :: (j +-> k) -> (i +-> j) -> (i +-> k)
data NegComp p q a c where
NegComp :: (forall b. Par (p a b) (q b c)) %1 -> NegComp p q a c
newtype Neg p a b = Neg (Not (p b a))
getNeg :: Neg p a b %1 -> Not (p b a)
getNeg (Neg f) = f
conv1 :: NegComp p q !~> Neg (Neg q :.: Neg p)
conv1 (NegComp e) = Neg \(Neg nq :.: Neg np) -> case e of Par e' -> e' (np, nq)
conv2 :: Neg (Neg q :.: Neg p) !~> NegComp p q
conv2 (Neg f) = NegComp (Par (\(np, nq) -> f (Neg nq :.: Neg np)))
asCocat :: (Neg p :.: Neg p !~> Neg p) -> p !~> NegComp p p
asCocat comp p = NegComp (Par \(np1, np2) -> getNeg (comp (Neg np2 :.: Neg np1)) p)