-
Notifications
You must be signed in to change notification settings - Fork 3
/
Categories.hs
552 lines (431 loc) · 17.1 KB
/
Categories.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
{-# LANGUAGE CPP #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
-- | Overloaded Categories, desugar @Arrow@ into classes in this module.
--
-- == Enabled with
--
-- @
-- {-\# OPTIONS -fplugin=Overloaded -fplugin-opt=Overloaded:Categories #-}
-- @
--
-- == Description
--
-- @Arrows@ notation - [GHC manual chapter](https://downloads.haskell.org/~ghc/8.10.1/docs/html/users_guide/glasgow_exts.html#arrow-notation) -
-- is cool, but it desugars into /"wrong"/ classes.
-- The 'arr' combinator is used for plumbing. We should desugar to proper
-- type-classes:
--
-- * 'CartesianCategory', not 'A.Arrow'
-- * 'CocartesianCategory', not 'A.ArrowChoice' (implementation relies on 'BicartesianCategory')
-- * 'CCC', not 'A.ArrowApply' (not implemented yet)
--
-- == Examples
--
-- Expression like
--
-- @
-- catAssoc
-- :: 'CartesianCategory' cat
-- => cat ('Product' cat ('Product' cat a b) c) ('Product' cat a ('Product' cat b c))
-- catAssoc = proc ((x, y), z) -> 'identity' -< (x, (y, z))
-- @
--
-- are desugared to (a mess which is)
--
-- @
-- 'fanout' ('proj1' '%%' 'proj1') ('fanout' ('proj2' '%%' 'proj1') 'proj2')
-- @
--
-- If you are familiar with arrows-operators, this is similar to
--
-- @
-- ('fst' . 'fst') '&&&' ('snd' . 'fst' '&&&' 'snd')
-- @
--
-- expression.
--
-- The @catAssoc@ could be instantiated to @cat = (->)@,
-- or more interestingly for example instantiate it to STLC morphisms to get an expression
-- like:
--
-- @
-- Lam (Pair (Fst (Fst (Var Here))) (Pair (Snd (Fst (Var Here))) (Snd (Var Here))))
-- @
--
-- @proc@ notation is nicer than writing de Bruijn indices.
--
-- This is very similar idea to Conal Elliott's [Compiling to Categories](http://conal.net/papers/compiling-to-categories/) work.
-- This approach is syntactically more heavy, but works in more correct
-- stage of compiler, before actual desugarer.
--
-- As one more example, we implement the automatic differentiation,
-- as in Conal's paper(s).
-- To keep things simple we use
--
-- @
-- newtype AD a b = AD (a -> (b, a -> b))
-- @
--
-- representation, i.e. use ordinary maps to represent linear maps.
-- We then define a function
--
-- @
-- evaluateAD :: Functor f => AD a b -> a -> f a -> (b, f b)
-- evaluateAD (AD f) x xs = let (y, f') = f x in (y, fmap f' xs)
-- @
--
-- which would allow to calculuate function value and
-- derivatives in given directions. Then we can define
-- simple quadratic function:
--
-- @
-- quad :: AD (Double, Double) Double
-- quad = proc (x, y) -> do
-- x2 <- mult -< (x, x)
-- y2 <- mult -< (y, y)
-- plus -< (x2, y2)
-- @
--
-- It's not as simple as writing @quad x y = x * x + y * y@,
-- but not /too far/.
--
-- Then we can play with it. At origo everything is zero:
--
-- @
-- let sqrthf = 1 / sqrt 2
-- in evaluateAD quad (0, 0) [(1,0), (0,1), (sqrthf, sqrthf)] = (0.0,[0.0,0.0,0.0])
-- @
--
-- If we evaluate at some other point, we see things working:
--
-- @
-- evaluateAD quad (1, 2) [(1,0), (0,1), (sqrthf, sqrthf)] = (5.0,[2.0,4.0,4.242640687119285])
-- @
--
-- Obviously, if we would use inspectable representation for linear maps,
-- as Conal describe, we'd get more benefits. And then 'arr' wouldn't
-- be definable!
--
module Overloaded.Categories (
-- * Category
C.Category,
identity,
(%%),
-- * Monoidial
SemigroupalCategory (..),
defaultAssoc, defaultUnassoc,
MonoidalCategory (..),
defaultLunit, defaultRunit, defaultUnrunit, defaultUnlunit,
CommutativeCategory (..),
defaultSwap,
-- * Product and Terminal
CartesianCategory (..),
CategoryWith1 (..),
-- * Coproduct and initial
CategoryWith0 (..),
CocartesianCategory (..),
-- * Bicartesian
BicartesianCategory (..),
-- * Closed cartesian category
CCC (..),
-- * Generalized element
GeneralizedElement (..),
-- * OPF
OPF (..),
-- * WrappedArrow and WrappedCategory
WrappedArrow (..),
WrappedCategory (..),
) where
import qualified Control.Arrow as A
import qualified Control.Category as C
import Control.Applicative (liftA2)
import Control.Arrow (Kleisli (..))
import Data.Functor.Contravariant (Op (..))
import Data.Kind (Type)
import Data.Profunctor (Star (..))
import Data.Semigroupoid.Dual (Dual (..))
import Data.Void (Void, absurd)
-------------------------------------------------------------------------------
-- Category
-------------------------------------------------------------------------------
-- | A non-clashing name for 'C.id'.
identity :: C.Category cat => cat a a
identity = C.id
{-# INLINE identity #-}
-- | A non-clashing name for @('C..')@.
(%%) :: C.Category cat => cat b c -> cat a b -> cat a c
(%%) = (C..)
{-# INLINE (%%) #-}
infixr 9 %%
-------------------------------------------------------------------------------
-- Monoidal
-------------------------------------------------------------------------------
class C.Category cat => SemigroupalCategory (cat :: k -> k -> Type) where
type Tensor cat :: k -> k -> k
assoc :: cat (Tensor cat (Tensor cat a b) c)
(Tensor cat a (Tensor cat b c))
unassoc :: cat (Tensor cat a (Tensor cat b c))
(Tensor cat (Tensor cat a b) c)
defaultAssoc :: (CartesianCategory cat, Tensor cat ~ Product cat) => cat (Tensor cat (Tensor cat a b) c) (Tensor cat a (Tensor cat b c))
defaultAssoc = fanout (proj1 %% proj1) (fanout (proj2 %% proj1) proj2)
defaultUnassoc :: (CartesianCategory cat, Tensor cat ~ Product cat) => cat (Tensor cat a (Tensor cat b c)) (Tensor cat (Tensor cat a b) c)
defaultUnassoc = fanout (fanout proj1 (proj1 %% proj2)) (proj2 %% proj2)
class SemigroupalCategory cat => MonoidalCategory (cat :: k -> k -> Type) where
type Unit cat :: k
lunit :: cat (Tensor cat (Unit cat) a) a
runit :: cat (Tensor cat a (Unit cat)) a
unlunit :: cat a (Tensor cat (Unit cat) a)
unrunit :: cat a (Tensor cat a (Unit cat))
defaultLunit :: (CartesianCategory cat, Tensor cat ~ Product cat) => cat (Tensor cat (Unit cat) a) a
defaultLunit = proj2
defaultRunit :: (CartesianCategory cat, Tensor cat ~ Product cat) => cat (Tensor cat a (Unit cat)) a
defaultRunit = proj1
defaultUnlunit :: (CategoryWith1 cat, Tensor cat ~ Product cat, Unit cat ~ Terminal cat) => cat a (Tensor cat (Unit cat) a)
defaultUnlunit = fanout terminal identity
defaultUnrunit :: (CategoryWith1 cat, Tensor cat ~ Product cat, Unit cat ~ Terminal cat) => cat a (Tensor cat a (Unit cat))
defaultUnrunit = fanout identity terminal
class SemigroupalCategory cat => CommutativeCategory cat where
swap :: cat (Tensor cat a b) (Tensor cat b a)
defaultSwap :: (CartesianCategory cat, Tensor cat ~ Product cat) => cat (Tensor cat a b) (Tensor cat b a)
defaultSwap = fanout proj2 proj1
-------------------------------------------------------------------------------
-- Product
-------------------------------------------------------------------------------
-- | Category with terminal object.
class CartesianCategory cat => CategoryWith1 (cat :: k -> k -> Type) where
type Terminal cat :: k
terminal :: cat a (Terminal cat)
-- | Cartesian category is a monoidal category
-- where monoidal product is the categorical product.
--
class C.Category cat => CartesianCategory (cat :: k -> k -> Type) where
type Product cat :: k -> k -> k
proj1 :: cat (Product cat a b) a
proj2 :: cat (Product cat a b) b
-- | @'fanout' f g@ is written as \(\langle f, g \rangle\) in category theory literature.
fanout :: cat a b -> cat a c -> cat a (Product cat b c)
instance CategoryWith1 (->) where
type Terminal (->) = ()
terminal _ = ()
instance CartesianCategory (->) where
type Product (->) = (,)
proj1 = fst
proj2 = snd
fanout f g x = (f x , g x)
instance CategoryWith1 Op where
type Terminal Op = Void
terminal = Op absurd
instance CartesianCategory Op where
type Product Op = Either
proj1 = Op inl
proj2 = Op inr
fanout (Op f) (Op g) = Op (fanin f g)
-------------------------------------------------------------------------------
-- Coproduct
-------------------------------------------------------------------------------
-- | Category with initial object.
class CocartesianCategory cat => CategoryWith0 (cat :: k -> k -> Type) where
type Initial cat :: k
initial :: cat (Initial cat) a
-- | Cocartesian category is a monoidal category
-- where monoidal product is the categorical coproduct.
--
class C.Category cat => CocartesianCategory (cat :: k -> k -> Type) where
type Coproduct cat :: k -> k -> k
inl :: cat a (Coproduct cat a b)
inr :: cat b (Coproduct cat a b)
-- | @'fanin' f g@ is written as \([f, g]\) in category theory literature.
fanin :: cat a c -> cat b c -> cat (Coproduct cat a b) c
instance CategoryWith0 (->) where
type Initial (->) = Void
initial = absurd
instance CocartesianCategory (->) where
type Coproduct (->) = Either
inl = Left
inr = Right
fanin = either
instance CategoryWith0 Op where
type Initial Op = ()
initial = Op (const ())
instance CocartesianCategory Op where
type Coproduct Op = (,)
inl = Op proj1
inr = Op proj2
fanin (Op f) (Op g) = Op (fanout f g)
-- | Bicartesian category is category which is
-- both cartesian and cocartesian.
--
-- We also require distributive morpism.
class (CartesianCategory cat, CocartesianCategory cat) => BicartesianCategory cat where
distr :: cat (Product cat (Coproduct cat a b) c)
(Coproduct cat (Product cat a c) (Product cat b c))
instance BicartesianCategory (->) where
distr (Left x, z) = Left (x, z)
distr (Right y, z) = Right (y, z)
-------------------------------------------------------------------------------
-- Dual
-------------------------------------------------------------------------------
instance CategoryWith1 cat => CategoryWith0 (Dual cat) where
type Initial (Dual cat) = Terminal cat
initial = Dual terminal
instance CategoryWith0 cat => CategoryWith1 (Dual cat) where
type Terminal (Dual cat) = Initial cat
terminal = Dual initial
instance CartesianCategory cat => CocartesianCategory (Dual cat) where
type Coproduct (Dual cat) = Product cat
inl = Dual proj1
inr = Dual proj2
fanin (Dual f) (Dual g) = Dual (fanout f g)
instance CocartesianCategory cat => CartesianCategory (Dual cat) where
type Product (Dual cat) = Coproduct cat
proj1 = Dual inl
proj2 = Dual inr
fanout (Dual f) (Dual g) = Dual (fanin f g)
-------------------------------------------------------------------------------
-- Exponential
-------------------------------------------------------------------------------
-- | Closed cartesian category.
--
class CartesianCategory cat => CCC (cat :: k -> k -> Type) where
-- | @'Exponential' cat a b@ represents \(B^A\). This is due how (->) works.
type Exponential cat :: k -> k -> k
eval :: cat (Product cat (Exponential cat a b) a) b
transpose :: cat (Product cat a b) c -> cat a (Exponential cat b c)
instance CCC (->) where
type Exponential (->) = (->)
eval = uncurry ($)
transpose = curry
-------------------------------------------------------------------------------
-- Generalized Element
-------------------------------------------------------------------------------
class C.Category cat => GeneralizedElement (cat :: k -> k -> Type) where
type Object cat (a :: k) :: Type
konst :: Object cat a -> cat x a
instance GeneralizedElement (->) where
type Object (->) a = a
konst = const
-------------------------------------------------------------------------------
-- Object-Preserving Functor
-------------------------------------------------------------------------------
-- | Object-Preserving Functor (from Hask).
-- Categories with combinator similar to arrows 'arr'.
class C.Category cat => OPF cat where
-- We could have @type OPFObj cat a :: k@
-- but it's hard to work with when we want object preserving functor.
opf :: (a -> b) -> cat a b
instance OPF (->) where
opf = id
-------------------------------------------------------------------------------
-- Star
-------------------------------------------------------------------------------
instance Monad m => CartesianCategory (Star m) where
type Product (Star m) = (,)
proj1 = Star (pure . proj1)
proj2 = Star (pure . proj2)
fanout (Star f) (Star g) = Star $ \a -> liftA2 (,) (f a) (g a)
instance Monad m => CategoryWith1 (Star m) where
type Terminal (Star m) = ()
terminal = Star (pure . terminal)
instance Monad m => CocartesianCategory (Star m) where
type Coproduct (Star m) = Either
inl = Star (pure . inl)
inr = Star (pure . inr)
fanin (Star f) (Star g) = Star (fanin f g)
instance Monad m => CategoryWith0 (Star m) where
type Initial (Star m) = Void
initial = Star (pure . initial)
instance Monad m => BicartesianCategory (Star m) where
distr = Star (pure . distr)
instance Monad m => CCC (Star m) where
type Exponential (Star m) = Star m
eval = Star $ uncurry runStar
transpose (Star f) = Star $ \a -> pure $ Star $ \b -> f (a, b)
instance Monad m => OPF (Star m) where
opf f = Star (pure . f)
-------------------------------------------------------------------------------
-- Kleisli
-------------------------------------------------------------------------------
instance Monad m => CartesianCategory (Kleisli m) where
type Product (Kleisli m) = (,)
proj1 = Kleisli (pure . proj1)
proj2 = Kleisli (pure . proj2)
fanout (Kleisli f) (Kleisli g) = Kleisli $ \a -> liftA2 (,) (f a) (g a)
instance Monad m => CategoryWith1 (Kleisli m) where
type Terminal (Kleisli m) = ()
terminal = Kleisli (pure . terminal)
instance Monad m => CocartesianCategory (Kleisli m) where
type Coproduct (Kleisli m) = Either
inl = Kleisli (pure . inl)
inr = Kleisli (pure . inr)
fanin (Kleisli f) (Kleisli g) = Kleisli (fanin f g)
instance Monad m => CategoryWith0 (Kleisli m) where
type Initial (Kleisli m) = Void
initial = Kleisli (pure . initial)
instance Monad m => BicartesianCategory (Kleisli m) where
distr = Kleisli (pure . distr)
instance Monad m => CCC (Kleisli m) where
type Exponential (Kleisli m) = Kleisli m
eval = Kleisli $ uncurry runKleisli
transpose (Kleisli f) = Kleisli $ \a -> pure $ Kleisli $ \b -> f (a, b)
instance Monad m => OPF (Kleisli m) where
opf f = Kleisli (pure . f)
-------------------------------------------------------------------------------
-- WrappedArrow
-------------------------------------------------------------------------------
-- | 'Arrow' correspond to various classes
-- in this hierarachy.
newtype WrappedArrow arr a b = WrapArrow { unwrapArrow :: arr a b }
instance C.Category arr => C.Category (WrappedArrow arr) where
id = WrapArrow identity
WrapArrow f . WrapArrow g = WrapArrow (f %% g)
instance A.Arrow arr => CategoryWith1 (WrappedArrow arr) where
type Terminal (WrappedArrow arr) = ()
terminal = WrapArrow (A.arr terminal)
instance A.Arrow arr => CartesianCategory (WrappedArrow arr) where
type Product (WrappedArrow arr) = (,)
proj1 = WrapArrow (A.arr proj1)
proj2 = WrapArrow (A.arr proj2)
fanout (WrapArrow f) (WrapArrow g) = WrapArrow (f A.&&& g)
instance A.ArrowChoice arr => CategoryWith0 (WrappedArrow arr) where
type Initial (WrappedArrow arr) = Void
initial = WrapArrow (A.arr absurd)
instance A.ArrowChoice arr => CocartesianCategory (WrappedArrow arr) where
type Coproduct (WrappedArrow arr) = Either
inl = WrapArrow (A.arr inl)
inr = WrapArrow (A.arr inr)
fanin (WrapArrow f) (WrapArrow g) = WrapArrow (f A.||| g)
instance A.ArrowChoice arr => BicartesianCategory (WrappedArrow arr) where
distr = WrapArrow (A.arr distr)
instance A.ArrowApply arr => CCC (WrappedArrow arr) where
type Exponential (WrappedArrow arr) = arr
eval = WrapArrow A.app
transpose = error "ArrowApply @(WrappedArrow arr) is not implemented"
instance A.Arrow arr => GeneralizedElement (WrappedArrow arr) where
type Object (WrappedArrow arr) a = a
konst = WrapArrow . A.arr . const
instance A.Arrow arr => OPF (WrappedArrow arr) where
opf = WrapArrow . A.arr
-------------------------------------------------------------------------------
-- WrappedCategory
-------------------------------------------------------------------------------
newtype WrappedCategory cat a b = WrapCat { unwrapCat :: cat a b }
instance C.Category cat => C.Category (WrappedCategory cat) where
id = WrapCat identity
WrapCat f . WrapCat g = WrapCat (f %% g)
instance (OPF cat, CartesianCategory cat, Product cat ~ (,))
=> A.Arrow (WrappedCategory cat)
where
arr = WrapCat . opf
WrapCat f *** WrapCat g = WrapCat (fanout (f %% proj1) (g %% proj2))
instance (OPF cat, CartesianCategory cat, Product cat ~ (,)
, CocartesianCategory cat, Coproduct cat ~ Either)
=> A.ArrowChoice (WrappedCategory cat)
where
WrapCat f +++ WrapCat g = WrapCat (fanin (inl %% f) (inr %% g))
instance (OPF cat, CartesianCategory cat, Product cat ~ (,)
, CCC cat, Exponential cat ~ cat)
=> A.ArrowApply (WrappedCategory cat)
where
app = WrapCat eval %% A.first (WrapCat (opf unwrapCat))