fmapping arrows over monads
Asked Answered
S

2

5

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.

Submergible answered 15/4, 2017 at 21:42 Comment(0)
S
7

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).

Sinful answered 15/4, 2017 at 22:20 Comment(0)
V
4

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.

Venial answered 16/4, 2017 at 0:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.