I have the following definitions for a monoidal category class (Similar to the standard library, but providing inverses of the necessary natural isomorphisms):
class (Category r, Category s, Category t) => Bifunctor p r s t | p r -> s t, p s -> r t, p t -> r s where
bimap :: r a b -> s c d -> t (p a c) (p b d)
--
class (Bifunctor b k k k) => Associative k b where
associate :: k (b (b x y) z) (b x (b y z))
associateInv :: k (b x (b y z)) (b (b x y) z)
--
class (Bifunctor b k k k) => HasIdentity k b i | k b -> i
class (Associative k b, HasIdentity k b i) => Monoidal k b i | k b -> i where
idl :: k (b i a) a
idr :: k (b a i) a
idlInv :: k a (b i a)
idrInv :: k a (b a i)
--
The problem with composing morphisms in a monoidal category using (.) is that the objects may be associated differently. For instance Monoidal Hask (,) (), we might want to compose a morphism of type x -> ((a, b), c) with a morphism of type ((a, ()), (b, c)) -> y. To make the types fit, the natural isomorphism given by bimap idrInv id . associate has to be applied.
Does the Haskell type system enable an automatic way of determining an appropriate isomorphism, based on the desired domain and and codomain type? I can't figure out how to do it.
I figured it out, sort of. The basic idea is to use a multi-parameter type class with a normalizing function and its inverse as methods. The normalizer associates everything to the right, recursively. The type class needs an instance for each case of the recursion. Then, to convert from one way to associate stuff to another, just compose the normalizer for the type of the first morphism and the inverse normalizer for the type of the second morphism.
I will link code here as soon as I will have published it.
Related
I have the category definition:
-- | Cat is the definition of a morphism
type Cat i = i -> i -> Type
-- | Vacuous Constraint
class Vacuous (p :: Cat i) (a :: i)
instance Vacuous p a
class Category (h :: Cat i) where
type Ob h :: i -> Constraint
type Ob h = Vacuous h
id :: Ob h a => h a a
(.) :: h b c -> h a b -> h a c
and I am trying to implement identity and associativity laws for it in QuickCheck, e.g.,
-- | Constraints
type Constraints h a b =
(
Category h, Ob h a,
Arbitrary (h a b), Eq (h a b), Show (h a b)
)
-- | Properties
prop_left_identity :: forall h a b. Constraints h a b => Property
prop_left_identity = property $ \(f:: h a b) -> f. id == f
Issue here is in Eq (h a b), if for instance h = (->), then I will have trouble defining Eq (a -> b) because function equality is not straightforward (I will have issue with unbounded types, and in case of bounded go through all possible inputs and I don't want that). So, I tried avoiding the point free notation and applying f to a, so I can only focus on Eq (b), but I don't have any means to do it. I need a function ap :: h a b -> a -> b but compiler gives an error because b is not of kind Type.
Any ideas about this please 🙏🏻
Bifunctors have a map function with this signature:
bimap :: (a -> b) -> (c -> d) -> p a c -> p b d
You could also have a map like this:
othermap :: ((a, c) -> (b, d)) -> p a c -> p b d
Types with this function are a strict subset of bifunctors (you can always define bimap using othermap but not vice versa). Is there a name for the second signature?
Follow-up: what about this intermediate function?
halfothermap :: ((a, c) -> b) -> (c -> d) -> p a c -> p b d
A type which is a Bifunctor need not have the same number of a values as b values. Consider, for example,
data TwoLists a b = TwoLists [a] [b]
It is easy to implement bimap, but othermap is a real problem, especially if one of the lists is empty.
othermap f (TwoLists [] (b:bs)) = TwoLists [] _
What can you do here? You need to call f to convert all the bs to a list of type [d], but you can only call that function if you have an a in hand.
Perhaps even worse is a type which doesn't really ever have b values at all:
data TaggedFunction k a b = TaggedFunction a (k -> b)
instance Bifunctor (TaggedFunction k) where
bimap f g (TaggedFunction a b) = TaggedFunction (f a) (g . b)
How can you implement othermap for this type? You could update the function, because you have an a in hand and will have a b by the time you need a d. But there's no way for you to replace the a with a c, because you can't get hold of a b to call othermap's function with.
So you can't put this function in Bifunctor. Perhaps you're asking, why not put this in a new class? I think leftroundabout is right that the class is too constraining to be useful. othermap can be defined only when you have the same number of as and bs in your structure, i.e. when your structure is some functor f wrapped around a tuple of type (a, b). For example, instead of TwoLists we could have defined
newtype PairList a b = PairList [(a, b)]
and that can have an othermap definition. But it would just be
othermap f (PairList vs) = PairList (fmap f vs)
Likewise instead of TaggedFunction we could have defined
newtype MultiFunction k a b = MultiFunction (k -> (a, b))
but the othermap definition is again just a wrapped call to fmap:
othermap f (MultiFunction g) = MultiFunction (fmap f g)
So perhaps the best way to imagine defining this abstraction would be as, not a typeclass function, but an ordinary function that operates over a type that captures this composition:
newtype TupleFunctor f a b = TupleFunctor (f (a, b))
othermap :: Functor f => ((a, b) -> (c, d))
-> TupleFunctor f a b -> TupleFunctor f c d
othermap f (TupleFunctor x) = TupleFunctor (fmap f x)
(Expanding on a comment by #glennsl...)
The type p a c "contains" both a and c.
But a function of type (a, c) -> (b, d) requires values of type a and c to be called, and a value of type p a c doesn't necessarily have both.
othermap #Either :: ((a, c) -> (b, d)) -> Either a c -> Either b d
othermap f (Left x) = ?
othermap f (Right y) = ?
There's no way for othermap to produce values of type b or d, because it can't call f in either case.
The type of the first argument implies a bifunctor that is isomorphic to (,), which is not true of all possible bifuntors. othermap is, in fact, strictly more specific than bimap. (There seems to be a contravariant relationship here that I won't try to make precise.)
As State monad can be factorized into Product (Left - Functor) and Reader (Right - Representable).
Is there a way to factorize Continuation Monad? Below code is my attempt, which wont type check
-- To form a -> (a -> k) -> k
{-# LANGUAGE MultiParamTypeClasses, TypeOperators, InstanceSigs, TypeSynonymInstances #-}
type (<-:) o i = i -> o
-- I Dont think we can have Functor & Representable for this type synonym
class Isomorphism a b where
from :: a -> b
to :: b -> a
instance Adjunction ((<-:) e) ((<-:) e) where
unit :: a -> (a -> e) -> e
unit a handler = handler a
counit :: (a -> e) -> e -> a
counit f e = undefined -- If we have a constraint on Isomorphism a e then we can implement this
Is there a list of Left & Rights Adjoints that form monads?
I have read that, given a pair of adjoints, they form a unique Monad & Comonad but, given a Monad, it can be Factorized into multiple Factors. Is there any example of this?
This doesn't typecheck because the class Adjunction only represents a small subset of adjunctions, where both functors are endofunctors on Hask.
As it turns out, this is not the case for the adjunction (<-:) r -| (<-:) r. There are two subtly different functors here:
f = (<-:) r, the functor from Hask to Op(Hask) (the opposite category of Hask, sometimes also denoted Hask^op)
g = (<-:) r, the functor from Op(Hask) to Hask
In particular, the counit should be a natural transformation in the Op(Hask) category, which flips arrows around:
unit :: a -> g (f a)
counit :: f (g a) <-: a
In fact, counit coincides with unit in this adjunction.
To capture this properly, we need to generalize the Functor and Adjunction classes so we can model adjunctions between different categories:
class Exofunctor c d f where
exomap :: c a b -> d (f a) (f b)
class
(Exofunctor d c f, Exofunctor c d g) =>
Adjunction
(c :: k -> k -> Type)
(d :: h -> h -> Type)
(f :: h -> k)
(g :: k -> h) where
unit :: d a (g (f a))
counit :: c (f (g a)) a
Then we get again that Compose is a monad (and a comonad if we flip the adjunction):
newtype Compose f g a = Compose { unCompose :: f (g a) }
adjReturn :: forall c f g a. Adjunction c (->) f g => a -> Compose g f a
adjReturn = Compose . unit #_ #_ #c #(->)
adjJoin :: forall c f g a. Adjunction c (->) f g => Compose g f (Compose g f a) -> Compose g f a
adjJoin = Compose . exomap (counit #_ #_ #c #(->)) . (exomap . exomap #(->) #c) unCompose . unCompose
and Cont is merely a special case of that:
type Cont r = Compose ((<-:) r) ((<-:) r)
See also this gist for more details: https://gist.github.com/Lysxia/beb6f9df9777bbf56fe5b42de04e6c64
I have read that given a pair of adjoints they form a unique Monad & Comonad but given a Monad it can be Factorized into multiple Factors. Is there any example of this?
The factorization is generally not unique. Once you've generalized adjunctions as above, then you can at least factor any monad M as an adjunction between its Kleisli category and its base category (in this case, Hask).
Every monad M defines an adjunction
F -| G
where
F : (->) -> Kleisli M
: Type -> Type -- Types are the objects of both categories (->) and Kleisli m.
-- The left adjoint F maps each object to itself.
: (a -> b) -> (a -> M b) -- The morphism mapping uses return.
G : Kleisli M -> (->)
: Type -> Type -- The right adjoint G maps each object a to m a
: (a -> M b) -> (M a -> M b) -- This is (=<<)
I don't know whether the continuation monad corresponds to an adjunction between endofunctors on Hask.
See also the nCatLab article on monads: https://ncatlab.org/nlab/show/monad#RelationToAdjunctionsAndMonadicity
Relation to adjunctions and monadicity
Every adjunction (L ⊣ R) induces a monad R∘L and a comonad L∘R. There is in general more than one adjunction which gives rise to a given monad this way, in fact there is a category of adjunctions for a given monad. The initial object in that category is the adjunction over the Kleisli category of the monad and the terminal object is that over the Eilenberg-Moore category of algebras. (e.g. Borceux, vol. 2, prop. 4.2.2) The latter is called the monadic adjunction.
I have a question about type parameters that I think is best expressed by an example. This piece of code
newtype Triple a b c = T (a,b,c)
instance Functor (Triple a b) where
fmap f (T (x, y, z)) = T (x, y, (f z))
expresses triples as functors in their third variable.
How would I turn them into functors in their second variable?
How would I turn actual tuples (not my new type) into a functor?
The general question is: suppose I have a parametric type m a b c d e how do I express the parametric type m a b d e obtained by fixing one parameter? Or equivalently, how do I express the parametric type m a b d e c obtained by making an arbitrary parameter the last one?
Edit: it may not have become quite clear what I mean, so I'm trying to clarify: Triple has kind * -> * -> * -> *. So I can partially evaluate at two types to get something of kind * -> * which could be Functor or some other parametrized class. This evaluation is easy to do at the first two parameters but it is in principle possible at any two of the parameters, and I am asking how it can be done. This is essentially asking for a flip on the level of types.
As a concrete use case I can have three parametrized classes Functor, Foo, and Bar, and I want (Triple _ b c) to be a Functor, (Triple a _ c) to be a Foo, and (Triple a b _) to be a Bar (for all a, b, c). So then Triple a b c would be a Functor, a Foo and a Bar. You would think of writing these one-parameter types a -> Triple a b c, b -> Triple a b c and c -> Triple a b c but of course this literal notation expresses mapping types.
Edit2: Before posting a question on stackoverflow I always try to strip it to its abstract core, but this seems to obscure what I actually want. So a concrete variant of this question can now be found here.
This is what newtypes are for. You wrap an existing type up in a newtype, letting you do different stuff to it at the type level while leaving the value level unchanged. For example:
newtype SecondTriple a b c = SecondTriple (a, c, b)
instance Functor (SecondTriple a b) where
fmap f (SecondTriple (x, z, y)) = SecondTriple (x, f z, y)
If you like, you can wrap Triple instead of wrapping (,,), but of course you can't use Triple's Functor instance anyway so it's not much help.
In this specific case you might get what you need by using lenses.
The combination of over and all the functions in the tuple module (_1, _2, _3 etc.) gives you the ability to lift functions into more tuple positions than just the rightmost one.
EDIT Adding an example.
So say we have this tuple.
(1, "Foo", True)
And we want to (+ 1) to the value in its first position.
> import Control.Lens (over, _1)
> over _1 (+ 1) (1, "Foo", True)
(2,"Foo",True)
Or upper-case the string in its second position
> import Data.Char (toUpper)
> import Control.Lens (over, _2)
> over _2 (map toUpper) (1, "Foo", True)
(1,"FOO",True)
Or perhaps we want to flip the bool in its third position
> import Control.Lens (over, _3)
> over _3 not (1, "Foo", True)
(1,"Foo",False)
A functor has kind Type -> Type, so Triple :: Type -> Type -> Type -> Type itself is not a functor; only the nearly-saturated partial application Triple a b for 2 types a and b can be a functor.
Triple is, however, an example of a "trifunctor", which you can define yourself.
class Trifunctor p where
trimap :: (a -> x) -> (b -> y) -> (c -> z) -> p a b c -> p x y z
-- There are only so many synonyms for first, second, etc
map13 :: (a -> x) -> p a b c -> p x y z
map13 f = trimap f id id
map23 :: (b -> y) -> p a b c -> p x y z
map23 f = trimap id f id
map33 :: (c -> z) -> p a b c -> p x y z
map33 f = trimap id id f
instance Trifunctor Triple where
trimap f g h (Triple x y z) = Triple (f x) (g y) (h z)
The pattern generalizes; the product of n types is an n-functor.
There is a bit of overlap between Bifunctor and Arrow methods:
class Bifunctor p where
first :: (a -> a') -> p a b -> p a' b
second :: (b -> b') -> p a b -> p a b'
bimap :: (a -> a') -> (b -> b') -> p a b -> p a' b'
class Arrow (~~>) where
...
first :: (a ~~> a') -> (a, b) ~~> (a', b)
second :: (b ~~> b') -> (a, b) ~~> (a, b')
(***) :: (a ~~> a') -> (b ~~> b') -> (a, b) ~~> (a', b')
The Bifunctor class comes with laws completely analogous to those of Functor.
The Arrow class comes with a number of laws different laws and a somewhat cryptic warning about (***): "Note that this is in general not a functor." Surprisingly (to me) there's only one law about (***):
first f >>> arr (id *** g) = arr (id *** g) >>> first f
The Arrow (->) instance and the Bifunctor (,) instance match up exactly, so that bimap #(,) = (***) #(->). Is there some special significance to this? Is there a meaningful hypothetical
class Foo (~~>) p where
biFoo :: (a ~~> a') -> (b ~~> b') -> p a b ~~> p a' b'
If so, does that admit functional dependencies?
Arrow is a (somewhat bastardized) precursor to a class of cartesian closed categories, or a least cartesian monoidal categories. Specifically, to monoidal categories whose tensor product is (,) and unit element ().
Recall that a monoidal category is characterised by the tensor product as a bifunctor, so there's your connection between Arrow and Bifunctor.
*** has in fact more laws than you listed, only, the library chooses to formulate those in terms of first instead. Here's an equivalent definition of the class:
class (Category k, Category k') => EnhancedCategory k k' where
arr :: k a b -> k' a b
-- arr id ≡ id
-- arr (f . g) = arr f . arr g
class (EnhancedCategory (->) a) => Arrow a where
(***) :: a b c -> a b' c' -> a (b,b') (c,c')
-- (f***id) . (g***id) ≡ (f.g)***id
-- (id***f) . (id***g) ≡ id***(f.g)
-- arr fst . (f***id) ≡ f . arr fst
-- arr snd . (id***g) ≡ g . arr snd
-- ¿ arr swap . (f***g) ≡ (g***f) . arr swap ?
-- ((f***g)***h) . assoc ≡ assoc . (f***(g***h))
diag :: a b (b,b)
first :: Arrow a => a b c -> a (b,d) (c,d)
first f = f***id
second :: Arrow a => a b c -> a (d,b) (d,c)
second g = id***g
(&&&) :: Arrow a => a b c -> a b d -> a b (c,d)
f&&&g = (f***g) . diag
Incidentally, it is also possible to remove the arr for lifting pure functions, and instead give the superclass only the dedicated methods fst, snd and assoc. I call that class Cartesian. This allows defining “arrow” categories that don't contain arbitrary Haskell functions; linear maps are an important example.
Arrow is equivalent to Strong + Category.
You can choose a different notion of strength to get a different kind of Arrow.
class Category a => ArrowChoice a where
arr :: (b -> c) -> a b c
(+++) :: a b c -> a b' c' -> a (Either b b') (Either c c')
In other words, the tensor product of your Cartesian closed category needn't be (,) exactly. Any tensor product you can come up with has a corresponding notion of strength, each of which would give you a corresponding variety of Arrow.
Notably, many profunctors are both Strong and Choice, so your Foo (which basically generalises Strong over a tensor product p) doesn't have a functional dependency.
The Control.Arrow module in base unfortunately muddles the hierarchy together a little bit (for example, their ArrowChoice has Arrow as a superclass).