I understand that an Arrow is a Profunctor, where one can transform its input and its output, but can one map an arrow over a Functor?
I understand that as-asked the answer is "no", since the fmap function type signature is (a -> b) -> f a -> f b and does not admit Arrow a b, but I hope what I'm asking is clear.
I am looking for a way to, for example, transform a Maybe input with an Arrow, where Nothing goes to Nothing and Just x goes to Just y where y is the result of applying the Arrow to x.
Arrow combines two concepts. One of them, as you say, is that of a profunctor, but first of all it's just a specific class of categories (as indeed the superclass evidences).
That's highly relevant for this question: yes, the signature of fmap is (a -> b) -> f a -> f b, but actually that is not nearly the full generality of what a functor can do! In maths, a functor is a mapping between two categories C and D, that assigns each arrow in C to an arrow in D. Arrows in different categories, that is! The standard Functor class merely captures the simplest special case, that of endofunctors in the Hask category.
The full general version of the functor class actually looks more like this (here my version from constrained-categories):
class (Category r, Category t) => Functor f r t | f r -> t, f t -> r where
fmap :: r a b -> t (f a) (f b)
Or, in pseudo-syntax,
class (Category (──>), Category (~>)) => Functor f (──>) (~>) where
fmap :: (a ──> b) -> f a ~> f b
This can sure enough also work when one of the categories is a proper arrow rather than an ordinary function category. For instance, you could define
instance Functor Maybe (Kleisli [] (->)) (Kleisli [] (->)) where
fmap (Kleisli f) = Kleisli mf
where mf Nothing = [Nothing]
mf (Just a) = Just <$> f a
to be used like
> runKleisli (fmap . Kleisli $ \i -> [0..i]) $ Nothing
[Nothing]
> runKleisli (fmap . Kleisli $ \i -> [0..i]) $ Just 4
[Just 0,Just 1,Just 2,Just 3,Just 4]
Not sure whether this would be useful for anything nontrivial, if using the standard profunctor-ish arrows. It is definitely useful in other categories which are not Hask-profunctors, for instance
instance (TensorSpace v) => Functor (Tensor s v) (LinearFunction s) (LinearFunction s)
expressing that you can map a linear function over a single factor of a tensor product (whereas it's generally not possible to map a nonlinear function over such a product – the result would depend on a choice of basis on the vector space).
I am looking for a way to, for example, transform a Maybe input with an arrow, where Nothing goes to Nothing and Just x goes to Just y where y is the result of applying the Arrow to x.
This can be implemented for specific Functors (such as Maybe), though ArrowChoice will likely be necessary:
maybeAmap :: ArrowChoice p => p a b -> p (Maybe a) (Maybe b)
maybeAmap p =
maybe (Left ()) Right
^>> returnA +++ p
>>^ const Nothing ||| Just
See Arrow equivalent of mapM? for a similar function written in proc-notation.
Speaking of mapM, profunctors has an interesting class called Traversing:
-- Abbreviated class definition:
class (Choice p, Strong p) => Traversing p where
traverse' :: Traversable f => p a b -> p (f a) (f b)
wander :: (forall f. Applicative f => (a -> f b) -> s -> f t) -> p a b -> p s t
The flag-bearer instance of Traversing is the one for the Star profunctor, which provides an alternative encoding of the familiar traverse function. Note that, while leftaroundabout's answer demonstrates a non-Hask functor for categories which are not necessarily Hask-profunctors, with Traversing we have a construction for Profunctors that do not necessarily have a Category instance.
Related
I've been wondering what a complete, all-encompassing context for instance Functor (f :.: g) would be. The immediate thought that pops into my head is:
newtype (f :.: g) a = Comp (f (g a))
instance (Functor f, Functor g) => Functor (f :.: g) where
fmap f (Comp x) = Comp (fmap (fmap f) x)
But then, two contravariant functors would also compose to be covariant, like so:
instance (Contravariant f, Contravariant g) => Functor (f :.: g) where
fmap f (Comp x) = Comp (contramap (contramap f) x)
Already not a promising beginning. However, I've also noticed that technically, f and g don't even have to have kind * -> * -- the only requirement for f :.: g :: * -> * is that f :: k -> * and g :: * -> k for some k. This means that non-functor types could compose to be functors, e.g.
newtype Fix f = Fix (f (Fix f))
instance Functor (Fix :.: (,)) where
fmap f (Comp x) = Comp (go x) where
go (Fix (x,xs)) = Fix (f x,go xs)
Fix :.: (,) is isomorphic to the Stream type:
data Stream a = a :> Stream a
so this does seem to be a non-trivial issue. This got me thinking -- if Haskell's Functor typeclass represents categorical functors from Hask to Hask, does that mean types like Fix and (,) could be functors working on some other categories? What would those categories be?
Yes, and we can read off exactly what sense that is from the shape of the constructor. Let's look at (,) first.
The (,) Functor
(,) :: * -> * -> *
This takes two types and produces a type. Up to isomorphism, this is equivalent to
(,) :: (*, *) -> *
i.e. we might as well uncurry the function and take both arguments at once. So (,) can be viewed as a functor for Hask × Hask to Hask, where Hask × Hask is the product category. We have a word for a functor whose domain is the product of two categories. We call it a bifunctor, and it's actually in base Haskell. Specifically, a bifunctor p is capable of turning maps from (a, b) to (a', b') in the product category into maps from p a b to p a' b'. Haskell's typeclass writes this in a slightly different but equivalent way
bimap :: Bifunctor p => (a -> b) -> (c -> d) -> p a c -> p b d
Having a map a -> b and a map c -> d is exactly equivalent to having a map (a, c) -> (b, d) in the product category. (What I mean by that is: the maps (a, c) -> (b, d) in the product category are defined to be products of maps a -> b and c -> d).
The Fix Functor
We can deal with Fix the same way.
newtype Fix f = Fix (f (Fix f))
Its shape is
Fix :: (* -> *) -> *
It takes a one-argument type constructor and produces a type.
Now, in Haskell, the * -> * part can be any one-argument type constructor, but categorically it's much nicer to work with functors. So I'm going to make the slightly stronger constraint (which it turns out we'll need in a minute) that the * -> * argument to Fix is a Functor, i.e. a functor from Hask to Hask.
In that case, Fix has the right shape to be a functor from the functor category Hask ^ Hask to the category Hask. A functor, categorically, takes objects to objects and arrows to arrows. So let's take that one step at a time.
The object part is easy, we've already defined it. Specifically, Fix takes the functor f (functors are the objects of a functor category; read that again if it doesn't make sense yet) and maps it to the type Fix f that we just defined.
Now, the arrows of a functor category are natural transformations. Given two functors f, g :: C -> D, a natural transformation α from f to g is a mapping from the objects of C to the arrows of D. Specifically, for every object x in the category C, α x should be an arrow in D going from f x to g x, with the following coherence condition:
For every arrow h : x -> y in C, we must have (g h) . (α x) === (α y) . (f h)
(Using notation such as function composition very loosely, in true category theory spirit)
Drawn as a commutative diagram, the following must commute,
Haskell doesn't really have a built-in type for natural transformations. With Rank-N types, we can write the correct shape
(forall a. f a -> g a)
This is the shape of a natural transformation, but of course we haven't verified the coherence property. So we'll just have to trust that it satisfies that property.
With all of this abstract nonsense in mind, if Fix is going to be a functor from Hask ^ Hask to Hask, it should take a natural transformation to an ordinary Haskell function, and it should have the following shape.
fixmap :: (Functor f, Functor g) => (forall a. f a -> g a) -> Fix f -> Fix g
Once we have this type, we can write the implementation fairly easily.
fixmap h (Fix inner) = Fix (h . fmap (fixmap h) $ inner)
or, equivalently (by the rules of natural transformations),
fixmap h (Fix inner) = Fix (fmap (fixmap h) . h $ inner)
I'm not aware of an idiomatic name for this shape of functor, nor am I aware of a typeclass that encompasses it, but of course nothing stops you from making it yourself.
I am looking for a function with type similar to:
Monad m => (a, b) -> (b -> m c) -> m (a, c)
It appears to me as some combination of bind (>>=) and a lens operation.
I am aware that I can solve this with a pattern match after a bind, but my gut tells me there is a "simpler" way to write this by leveraging lenses.
Is there any such operation?
This is definitely lensy. The monad is actually just a bit of a distraction because all you need is a functor:
changesecond (a, b) f = fmap (a,) (f b)
I'm pretty sure the _2 lens can be made to do your bidding with a basic lens thing like maybe over but I'm not too familiar with the library yet.
Edit
No combinator is really needed. You can write
changesecond pair f = _2 f pair
You should be able to work this out from the general definition of the Lens type.
Edit 2
This simple example demonstrates the main theme of Van Laarhoven lens construction:
Extract the focus from the context.
Apply the given function to produce a functorful of results.
Use fmap to restore the contexts to the results.
Ed Kmett's lens library elaborates on this theme in various ways. Sometimes it strengthens the functor constraint. Sometimes it generalizes the function to a profunctor. In the case of Equality, it removes the functor constraint. It just turns out that the same basic type shape can express a lot of different ideas.
Your function is just forM = flip mapM, or for = flip traverse if you relax the Monad constraint to Applicative. The Functor being traversed is (,) a.
Prelude> let foo :: Applicative f => (a, b) -> (b -> f c) -> f (a, c); foo p k = traverse k p
Prelude> :t foo
foo :: Applicative f => (a, b) -> (b -> f c) -> f (a, c)
Prelude> foo (1,2) (\x -> [x,2*x])
[(1,2),(1,4)]
(Also, as dfeuer points out, you don't even need Applicative in this specific case.)
It appears that type classes such as Applicative, Monad and Arrow have some sort of sum type equivalent in type classes such as Alternative, MonadPlus and ArrowPlus respectively. For example, Applicative and Alternative can be used to define the following:
(<&&>) :: Applicative f => f a -> f b -> f (a, b)
a <&&> b = (,) <$> a <*> b
(<||>) :: Alternative f => f a -> f b -> f (Either a b)
a <||> b = (Left <$> a) <|> (Right <$> b)
However, in all of these cases (as well as with ArrowChoice), the product type class is a prerequisite for the sum type class. Are there type class rules or common functions that depend on the prerequisite class? The Typeclassopedia touches on these relationships, but unfortunately I couldn't find any explicit reason for the dependency.
Arrow is basically the class for monoidal categories1 – with “monoid” not referring to Monoid, but the product-monoid of Haskell types. I.e., with unit element () and multiplication (,). Now, sum types make up a monoid as well, and that's what ArrowChoice uses. These two classes are in that sense complementary; ArrowChoice shouldn't really be a subclass of Arrow.
In a monoidal category, you can then go on to have monoidal functors. How these come out depends on what you use as your type-monoid. For (), (,), you get
class ProdMonoidalFtor f where
prodUnit :: () -> f ()
prodZip :: (f a, f b) -> f (a,b)
type (+) = Either
class SumMonoidalFtor f where
sumUnit :: Void -> f Void
sumZip :: f a + f b -> f (a+b)
Turns out the latter is basically useless, because Void is the initial object of Hask, meaning there exists exactly one Void -> a (namely absurd) for all types a. However, what does make some sense is comonoidal functors with +:
class SumCoMonoidalFtor f where
sumCounit :: f Void -> Void -- I bet you find this useless too, but it's not totally.
sumCozip :: f (a+b) -> f a + f b
That in turn wouldn't make sense for product types, because () is the terminal object.
What's interesting now is that ProdMonoidalFtor is equivalent to Applicative:
instance (ProdMonoidalFtor f) => Applicative f where
pure x = fmap (const x) $ prodUnit ()
fs <*> xs = fmap (\(f,x) -> f x) $ prodZip (fs,xs)
One might then suspect that Alternative is equivalent to SumMonoidalFtor, but it's not! Actually, it is equivalent to decisive functors, which are to comonads as applicatives are to monads.
Whereas Alternative and MonadPlus don't really seem to have much mathematical backing, they're essentially what you get when “un-Kleisliing” the ArrowChoice class, but using the Kleisli category that arises from ProdMonoidalFtor. It's all a bit dubious.
1That's considering only first/left, second/right, and ***/+++. As for the remaining &&&, ||| and arr, these are more specific and IMO belong in seperate classes.
Looking at the Haskell documentation, lifting seems to be basically a generalization of fmap, allowing for the mapping of functions with more than one argument.
The Wikipedia article on lifting gives a different view however, defining a "lift" in terms of a morphism in a category, and how it relates to the other objects and morphisms in the category (I won't give the details here). I suppose that that could be relevant to the Haskell situation if we are considering Cat (the category of categories, thus making our morphisms functors), but I can't see how this category-theoretic notion of a lift relates the the one in Haskell based on the linked article, if it does at all.
If the two concepts aren't really related, and just have a similar name, are the lifts (category theory) used in Haskell at all?
Lifts, and the dual notion of extensions, are absolutely used in Haskell, perhaps most prominently in the guise of comonadic extend and monadic bind. (Confusingly, extend is a lift, not an extension.) A comonad w's extend lets us take a function w a -> b and lift it along extract :: w b -> b to get a map w a -> w b. In ASCII art, given the diagram
w b
|
V
w a ---> b
where the vertical arrow is extract, extend gives us a diagonal arrow (making the diagram commute):
-> w b
/ |
/ V
w a ---> b
More familiar to most Haskellers is the dual notion of bind (>>=) for a monad m. Given a function a -> m b and return :: a -> m a, we can "extend" our function along return to get a function m a -> m b. In ASCII art:
a ---> m b
|
V
m a
gives us
a ---> m b
| __A
V /
m a
(That A is an arrowhead!)
So yes, extend could have been called lift, and bind could have been called extend. As for Haskell's lifts, I have no idea why they're called that!
EDIT: Actually, I think that again, Haskell's lifts are actually extensions. If f is applicative, and we have a function a -> b -> c, we can compose this function with pure :: c -> f c to get a function a -> b -> f c. Uncurrying, this is the same as a function (a, b) -> f c. Now we can also hit (a, b) with pure to get a function (a, b) -> f (a, b). Now, by fmaping fst and snd, we get a functions f (a, b) -> f a and f (a, b) -> f b, which we can combine to get a function f (a, b) -> (f a, f b). Composing with our pure from before gives (a, b) -> (f a, f b). Phew! So to recap, we have the ASCII art diagram
(a, b) ---> f c
|
V
(f a, f b)
Now liftA2 gives us a function (f a, f b) -> f c, which I won't draw because I'm sick of making terrible diagrams. But the point is, the diagram commutes, so liftA2 actually gives us an extension of the horizontal arrow along the vertical one.
"Lifting" comes up many times in functional programming, not only in fmap but in many other contexts. Examples of "liftings" include:
fmap :: (a -> b) -> F a -> F b where F is a functor
cmap :: (b -> a) -> F a -> F b where F is a contrafunctor
bind :: (a -> M b) -> M a -> M b where M is a monad
ap :: F (a -> b) -> F a -> F b where F is an applicative functor
point :: (_ -> a) -> _ -> F a where F is a pointed functor
filtMap :: (a -> Maybe b) -> F a -> F b where F is a filterable functor
extend :: (M a -> b) -> M a -> M b where M is a comonad
Other examples include applicative contrafunctor, filterable contrafunctor, and co-pointed functor.
All these type signatures are similar in one way: they map one kind of function between a and b into another kind of function between a and b.
In these different cases, the function types are not simply a -> b but have some kind of "twisted" types: e.g. a -> M b or F (a -> b) or M a -> b or F a -> F b and so on. However, each time the laws are very similar: twisted function types need to have identity and composition laws, and twisted composition needs to be associative.
For example, for applicative functors, we need to be able to compose functions of type F (a -> b). So we need to define a special "twisted" identity function (pure id :: F (a -> a) ) and a "twisted" composition operation, call it apcomp, with type signature F (a -> b) -> F (b -> c) -> F (a -> c). This operation needs to have identity and associativity laws. The ap operation needs to have identity and composition laws ("twisted identity maps to twisted identity" and "twisted composition maps to twisted composition").
Once we go through all these examples and derive the laws, we can prove that the laws turn out to be the same in all cases, if we formulate the laws via the "twisted" operations.
This is because we can formulate all these operations as functors in the sense of category theory. For example, for the applicative functor, we define two categories: the F-applicative category (objects a, b, ..., morphisms F(a -> b)) and the F-lifted category (objects F a, F b, ..., morphisms F a -> F b). A functor between these two categories requires us to have a lifting of morphisms, ap :: F(a -> b) -> F a -> F b. The laws of ap are completely equivalent to the standard laws of that functor.
Similar arguments hold for other typeclasses. We need to define categories, morphisms, composition operations, identity morphisms, and functors in each case. Once we verify that the laws hold, we will see that each of these typeclasses has an associated pair of categories and a functor between them, such that the laws of the typeclass are equivalent to the laws of these categories and the functor.
What have we gained? We have formulated the laws of many typeclasses in the same way (as the laws of categories and functors). This is a great economy of thought: we don't need to memorize all these laws each time; we can just memorize which categories and which functors need to be written down for each typeclass, as long as the methods of the typeclass can be reduced to some kind of "twisted lifting".
In this way, we can say that "liftings" are important and provide an application of category theory in functional programming.
I have made a presentation about this, https://www.youtube.com/watch?v=Zau8CxsfxOo and I'm writing a new free book where all derivations will be shown. https://github.com/winitzki/sofp
I would like to have a function for either mapping a pure function to a container or sequencing applicative/monadic action through it. For pure mapping we have
fmap :: Functor f => (a -> b) -> (f a -> f b)
For monadic sequencing we have (from Data.Taversable)
mapM :: (Traversable f, Monad m) => (a -> m b) -> (f a -> m (f b))
Which is similar to
mapKleisli :: (Traversable f, Monad m) => Kleisli m a b -> Kleisli m (f a) (f b)
mapKleisli = Kleisli . mapM . runKleisli
We know both (->) and (Kleisli m) are categories (and arrows). So it's naturally to make a generalization:
mapCategory :: (X f, Category c) => c a b -> c (f a) (f b)
Do you know such a class X with similar method? Maybe, somewhere on hackage? I tried to hoogle/hayoo but haven't found anything appropriate.
Update:
Now I know better what I need. Both Kleisli arrows and (->) are instances of ArrowApply which is as powerful as Monad. I came up with this arrow-based version of Travesable:
{-# LANGUAGE TypeOperators #-}
import Prelude hiding (id, (.), mapM)
import Control.Arrow
import Control.Category
class Traversable f where
traverse :: ArrowApply (~>) => f a -> (a ~> b) ~> f b
mapArrow :: (ArrowApply (~>), Traversable f) => a ~> b -> f a ~> f b
mapArrow a = arr (\x -> (traverse x, a)) >>> app
instance Traversable Maybe where
traverse Nothing = arr (const Nothing)
traverse (Just x) = arr (\a -> (a, x)) >>> app >>> arr Just
instance Traversable [] where
traverse [] = arr (const [])
traverse (x : xs) = undefined -- this is hard!
I could use just usual Applicative-based Traversable, with Identity for pure functions, but I'm not sure it is good. Considering pure functions as special case of monadic actions is weird. Interpreting both pure functions and monadic actions as instances of some action class (Category/Arrow/ArrowApply) looks more straightforward to me.
Questions: would you like to finish instance for []? Has my opinion about ArrowApply vs Monad any sense?
You're asking for "some class X", but it should be pretty clear that the most (or perhaps, only) correct name for this class would be "Functor". What you want is simply a functor class defined for an arbitrary Category instance, rather than limited to (->).
Of course, your definition is still limited to (endo)functors from a category to a subcategory defined by the type constructor giving the instance. If you generalize a bit further, there's no reason for the two categories to be the same, giving you a type class something like this one:
class (Category r, Category t) => Functor f r t | f r -> t, f t -> r where
fmap :: r a b -> t (f a) (f b)
This is still awfully limited vs. the full concept of a functor in category theory, but oh well.
It's also interesting to observe that this still has a (->) type constructor in it--that's because, even though we're modeling the source and target categories with arbitrary instances, the whole thing (and in particular, the functor itself) still exists in some sense in Hask, i.e., the category associated with (->). The other half of the functor (the part mapping objects) is, roughly speaking, the (->) in the kind * -> * for the type constructor f.