How (if at all) does the exponential interpretation of (->) (a -> b as ba) generalize to categories other than Hask/Set? For example it would appear that the interpretation for the category of non-deterministic functions is roughly Kliesli [] a b as 2a * b (a -> b -> Bool).
The notion of exponential can be defined in general terms, beyond Hask/Set. A category with exponentials and products is called a cartesian closed category. This is a key notion in theoretical computer science since each c.c. category is essentially a model of the typed lambda calculus.
Roughly, in a cartesian closed category for any pair of objects a,b there exist:
a product object (a * b), and
an exponential object (b^ab)
with morphisms
eval : (b^a)*a -> b (in Haskell: \(f,x) -> f x, AKA apply)
for any f : (a*b)->c, there exists Lf : a -> (c^b) (in Haskell: curry f)
satisfying the equation "they enjoy in the lambda calculus", i.e., if f : (a*b)->c, then:
f = (Lf * id_a) ; eval
In Haskell, the last equation is:
f = \(x :: (a,b), y :: a) -> apply (curry f x, id y) where apply (g,z) = g z
or, using arrows,
f = (curry f *** id) >>> apply where apply (g,z) = g z
Related
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.
Is there any standard name for a type constructor F :: * -> * -> * -> * with operations
return :: x -> F a a x
bind :: F a b x -> (x -> F b c y) -> F a c y
that is a contravariant functor in the first argument and a covariant functor in the second and third? In particular, does this correspond to any kind construction in category theory?
The operations give rise to a
join :: F a b (F b c x) -> F a c x
operation that makes this seem like some kind of "category in the category of endofunctors", but I'm not sure how that could be formalised.
EDIT: As chi points out, this is related to the indexed monad: given an indexed monad
F' : (* -> *) -> (* -> *)
we can use the Atkey construction
data (:=) :: * -> * -> * -> *
V :: x -> (x := a) a
and then define
F a b x = F' (x := b) a
to get the kind of monad we want. I've done the construction in Agda to check. I'd still like to know whether this more limited structure is known, though.
As pointed out in the comments, this is the notion of a Parametrised Monad introduced by Robert Atkey in his Parametrised Notions of Computation paper. This corresponds to the notion of a category enriched over a category of endofunctors in category theory.
For a category C to be enriched over a category V with monoidal structure (I, x) means that for every objects X, Y of C, the Hom-object Hom(X, Y) is an object of V, and there exist morphisms that give the identity and composition, I -> Hom(X, X) and Hom(Y, Z) x Hom(X, Y) -> Hom(X, Z). Certain idenity and associativity conditions must hold, corresponding to the usual requirements of identity and associativity for a category.
A monad M on C can be seen as a one-object category enriched over endofunctors on C. Since there is only one object X, there is also one Hom-object Hom(X, X), which is M. The return operation gives rise to an identity morphism, a natural transformation I -> M, and the join operation gives rise to a composition morphism, a natural transformation M x M -> M. The identity and associativity conditions then correspond exactly to those of a monad.
A parametrised monad M on C with parameters taken from some set S can be seen as a category with elements of S as objects, enriched over the endofunctors of C. The Hom-object Hom(X, Y) is M X Y and the return and join operations described in the question again give rise to the families of morphisms required.
> {-# LANGUAGE RankNTypes #-}
I was wondering if there was a way to represent the axiom of choice in haskell and/or some other functional programming language.
As we know, false is represented by the type with no values (Void in haskell).
> import Data.Void
We can represent negation like so
> type Not a = a -> Void
We can express the law of excluded middle for a type a like so
> type LEM a = Either a (Not a)
This means we can make classical logic into a Reader monad
> type Classical a = (forall r. LEM r) -> a
We can, for example, do double negation elimination in it
> doubleneg :: Classical (Not (Not a) -> a)
> doubleneg = \lem nna -> either id (absurd . nna) lem
We can also have a monad where the law of excluded middle fails
> type AntiClassical a = Not (forall r. LEM r) -> a
Now the question is, how can we make a type that represents the axiom of choice? The axiom of choice talks about sets of sets. This implies we would need types of types or something. Is there something equivalent to the axiom of choice that could be encoded? (If you can encode the negation, just combine it with the law of excluded middle). Maybe trickery would allow us to have types of types.
Note: Ideally, it should be a version of the axiom of choice that works with Diaconescu's theorem.
This is just a hint.
The axiom of choice can be expressed as:
If for every x : A there's a y : B such that the property P x y holds, then there's a choice function f : A -> B such that, for all x : A we have P x (f x).
More precisely
choice : {A B : Set} (P : A -> B -> Set) ->
((x : A) -> Σ B (λ y -> P x y)) ->
Σ (A -> B) (λ f -> (x : A) -> P x (f x))
choice P h = ?
given
data Σ (A : Set) (P : A -> Set) : Set where
_,_ : (x : A) -> P x -> Σ A P
Above, choice is indeed provable. Indeed, h assigns to each x a (dependent) pair whose first component y is an element of A and the second component is a proof that the first indeed satisfies P x y. Instead, the f in the thesis must assign to x only y, without any proof.
As you see, obtaining the choice function f from h is just a matter of discarding the proof component in the pair.
There's no need to extend Agda with LEM or any other axiom to prove this. The above proof is entirely constructive.
If we were using Coq, note that Coq forbids to eliminate a proof (such as h : ... -> Prop) to construct a non-proof (f), so translating this into Coq directly fails. (This is to allow program extraction.) However, if we avoid the Prop sort of Coq and use Type directly, then the above can be translated.
You may want to use the following projections for this exercise:
pr1 : ∀ {A : Set} {P} -> Σ A P -> A
pr1 (x , _) = x
pr2 : ∀ {A : Set} {P} -> (p : Σ A P) -> P (pr1 p)
pr2 (_ , y) = y
I am trying to reconcile the math notion of functor and the haskell notion of it.
This article http://brianshourd.com/posts/2012-10-26-tilt-functors-in-haskell.html explains a bit, but maybe someone can provide an example for the following case:
Suppose i define the following categories
Add: where objects are 0,1,2,3,...., morphisms are (0+), (1+), (2+), ... (0+) is the id morph.
Mul: where objects are 0,1,2,3,...., morphisms are (0*), (1*), (2*), ... (1*) is the id morph.
The composition is (.)
How would one define a functor between these two categories in Haskell (if that is possible and if what i described above are categories at all)
Thank you
EDIT: As per #chi suggestion, i am clarifying the question a bit. I am more interested in how you would put/translate into Haskell any functor between these two categories (as opposed to the existence of one, for example map any number to 42 and any morphism to (1*) as #chi sugested)
EDIT 2: Another way to ask this is, how do you reconcile these two statements "the Functor typeclass, which is basically for things that can be mapped over" with "Given categories C and D, a functor F:C→D is a rule that assigns to each object A in C an object F(A) in D. It also assigns to every morphism f:A→B of objects of C a morphism F(f):F(A)→F(B)". If the type constructor [] (lists) is a functor, what are the two categories it connects and how exactly is it mapping objects and morphisms between those two categories
The issue is that Haskell's Functor only represents a certain kind of functor: functors from the category of Haskell types and functions (Hask) to itself. Functor maps types to types (the objects of Hask) and functions to functions (the morphisms of Hask). For instance, the Maybe Functor maps any given type a to the type Maybe a and it uses fmap to map a given function a -> b to a function Maybe a -> Maybe b.
In the category of Hask, the objects are types and the morphisms are functions. Your category has integers as its objects and as a result doesn't fit the pattern of Functor, so the question doesn't quite make sense to ask in this form, at least not with regard to the Functor type class.
If you look at the definition of the class Category in Haskell, you would see this.
class Category cat where
id :: cat a a
(.) :: cat b c -> cat a b -> cat a c
See, if the objects in the category are Ints, then you would need to define those functions where a, b and c are Ints. This is fine when objects in the collection are types but not values. So you can't make them an instance of Category.
Also looking at fmap:
fmap :: (a -> b) -> f a -> f b
You can see it only makes sense with types (thus in the category Hask). If the objects are integers, you would pass morphisms like 4 --> 5 and -> only works with types.
Then (0+) is a morphism from Int to Int. You would need a morphism from, say, 2 to 5.
Maybe it would be something like this:
data IntMorphism = IdMorphism | IntMorphism Int Int
comp :: IntMorphism -> IntMorphism -> IntMorphism
comp IdMorphism m = m
comp m IdMorphism = m
comp (IntMorphism y' z) (IntMorphism x y)
| y /= y' = error "can't compose"
| otherwise = IntMorphism x z
f1 :: Int -> Int
f1 = (+5)
f2 :: IntMorphism -> IntMorphism
f2 IdMorphism = IdMorphism
f2 (IntMorphism x y) = IntMorphism (f1 x) (f1 y)
And the functor laws hold. In particular:
f2 (comp (IntMorphism y z) (IntMorphism x y))
= f2 (IntMorphism x z)
= IntMorphism (f1 x) (f1 z)
= comp (IntMorphism (f1 y) (f1 z)) (IntMorphism (f1 x) (f1 y))
= comp (f2 (IntMorphism y z)) (f2 (IntMorphism x y))
Of course, as it deals with values, it can't use the typeclasses defined in base and it is all done at runtime.
From page 3 of http://research.microsoft.com/en-us/um/people/emeijer/Papers/meijer94more.pdf:
it is not true in general that catamorphisms are closed under composition
Under what conditions do catamorphisms compose to a catamorphism? More specifically (assuming I understood the statement correctly):
Suppose I have two base functors F and G and folds for each: foldF :: (F a -> a) -> (μF -> a) and foldG :: (G a -> a) -> (μG -> a).
Now suppose I have two algebras a :: F μG -> μG and b :: G X -> X.
When is the composition (foldG b) . (foldF a) :: μF -> X a catamorphism?
Edit: I have a guess, based on dblhelix's expanded answer: that outG . a :: F μG -> G μG must be the component at μG of some natural transformation η :: F a -> G a. I don't know whether this is right. (Edit 2: As colah points out, this is sufficient but not necessary.)
Edit 3: Wren Thornton on Haskell-Cafe adds: "If you have the right kind of distributivity property (as colah suggests) then things will work out for the particular case. But, having the right kind of distributivity property typically amounts to being a natural transformation in some appropriately related category; so that just defers the question to whether an appropriately related category always exists, and whether we can formalize what "appropriately related" means."
When is the composition (fold2 g) . (fold1 f) :: μF1 -> A a catamorphism?
When there exists an F1-algebra h :: F1 A -> A such that fold1 h = fold2 g . fold1 f.
To see that catamorphisms are in general not closed under composition, consider the following generic definitions of type-level fixed point, algebra, and catamorphism:
newtype Fix f = In {out :: f (Fix f)}
type Algebra f a = f a -> a
cata :: Functor f => Algebra f a -> Fix f -> a
cata phi = phi . fmap (cata phi) . out
For catamorphisms to compose we would need
algcomp :: Algebra f (Fix g) -> Algebra g a -> Algebra f a
Now try writing this function. It takes two functions as arguments (of types f (Fix g) -> Fix g and g a -> a respectively) and a value of type f a, and it needs to produce a value of type a. How would you do that? To produce a value of type a your only hope is to apply the function of type g a -> a, but then we are stuck: we have no means to turn a value of type f a into a value of type g a, have we?
I am not sure whether this is of any use for your purposes, but an example of a condition under which one can compose to catamorphisms is if we have a morphism from the result of the second cata to the fixed point of the second functor:
algcomp' :: (Functor f, Functor g) =>
(a -> Fix g) -> Algebra f (Fix g) -> Algebra g a -> Algebra f a
algcomp' h phi phi' = cata phi' . phi . fmap h
(Disclaimer: This is outside my area of expertise. I believe I'm correct (with caveats provided at different points), but ... Verify it yourself.)
A catamorphism can be thought of as a function that replaces constructors of a data type with other functions.
(In this example, I will be using the following data types:
data [a] = [] | a : [a]
data BinTree a = Leaf a | Branch (BinTree a) (BinTree a)
data Nat = Zero | Succ Nat
)
For example:
length :: [a] -> Nat
length = catamorphism
[] -> 0
(_:) -> (1+)
(Sadly, the catamorphism {..} syntax is not available in Haskell (I saw something similar in Pola). I've been meaning to write a quasiquoter for it.)
So, what is length [1,2,3]?
length [1,2,3]
length (1 : 2 : 3 : [])
length (1: 2: 3: [])
1+ (1+ (1+ (0 )))
3
That said, for reasons that will become apparent later, it is nicer to define it as the trivially equivalent:
length :: [a] -> Nat
length = catamorphism
[] -> Zero
(_:) -> Succ
Let's consider a few more example catamorphisms:
map :: (a -> b) -> [a] -> b
map f = catamorphism
[] -> []
(a:) -> (f a :)
binTreeDepth :: Tree a -> Nat
binTreeDepth = catamorphism
Leaf _ -> 0
Branch -> \a b -> 1 + max a b
binTreeRightDepth :: Tree a -> Nat
binTreeRightDepth = catamorphism
Leaf _ -> 0
Branch -> \a b -> 1 + b
binTreeLeaves :: Tree a -> Nat
binTreeLeaves = catamorphism
Leaf _ -> 1
Branch -> (+)
double :: Nat -> Nat
double = catamorphism
Succ -> Succ . Succ
Zero -> Zero
Many of these can be nicely composed to form new catamorphisms. For example:
double . length . map f = catamorphism
[] -> Zero
(a:) -> Succ . Succ
double . binTreeRightDepth = catamorphism
Leaf a -> Zero
Branch -> \a b -> Succ (Succ b)
double . binTreeDepth also works, but it is almost a miracle, in a certain sense.
double . binTreeDepth = catamorphism
Leaf a -> Zero
Branch -> \a b -> Succ (Succ (max a b))
This only works because double distributes over max... Which is pure coincidence. (The same is true with double . binTreeLeaves.) If we replaced max with something that didn't play as nicely with doubling... Well, let's define ourselves a new friend (that doesn't get along as well with the others). For a binary operators that double doesn't distribute over, we'll use (*).
binTreeProdSize :: Tree a -> Nat
binTreeProdSize = catamorphism
Leaf _ -> 0
Branch -> \a b -> 1 + a*b
Let's try to establish sufficient conditions for two catamorphisms two compose. Clearly, any catamorphism will quite happily be composed with length, double and map f because they yield their data structure without looking at the child results. For example, in the case of length, you can just replace Succ and Zero with what ever you want and you have your new catamorphism.
If the first catamorphism yields a data structure without looking at what happens to its children, two catamorphisms will compose into a catamorphism.
Beyond this, things become more complicated. Let's differentiate between normal constructor arguments and "recursive arguments" (which we will mark with a % sign). So Leaf a has no recursive arguments, but Branch %a %b does. Let's use the term "recursive-fixity" of a constructor to refer to the number of recursive arguments it has. (I've made up both these terms! I have no idea what proper terminology is, if there is one! Be wary of using them elsewhere!)
If the first catamorphism maps something into a zero recursive fixity constructor, everything is good!
a | b | cata(b.a)
===============================|=========================|================
F a %b %c .. -> Z | Z -> G a b .. | True
If we map children directly into a new constructor, we're also good.
a | b | cata(b.a)
===============================|=========================|=================
F a %b %c .. -> H %c %d .. | H %a %b -> G a b .. | True
If we map into a recursive fixity one constructor...
a | b | cata(b.a)
===============================|=========================|=================
F a %b %c .. -> A (f %b %c..) | A %a -> B (g %a) | Implied by g
| | distributes over f
But it isn't iff. For example, if there exist g1 g2 such that g (f a b..) = f (g1 a) (g2 b) .., that also works.
From here, the rules will just get messier, I expect.
Catamorphisms de-construct a data structure into a result value. So, in general, when you apply a catamorphism, the result is something completely different, and you cannot apply another catamorphism to it.
For example, a function that sums all elements of [Int] is a catamorphism, but the result is Int. There is no way how to apply another catamorphism on it.
However, some special catamorphisms create a result of the same type as the input. One such example is map f (for some given function f). While it de-constructs the original structure, it also creates a new list as its result. (Actually, map f can be viewed both as a catamorphism and as an anamorphism.) So if you have such a class of special catamorphisms, you can compose them.
If we consider semantic equivalence, the composition of two catamorphisms is a catamorphism, when the first one is a hylomorphism:
cata1 . hylo1 = cata2
For example (Haskell):
sum . map (^2) = foldl' (\x y -> x + y^2) 0