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, whereNothing
goes toNothing
andJust x
goes toJust y
wherey
is the result of applying the Arrow tox
.
This can be implemented for specific Functor
s (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 Profunctor
s that do not necessarily have a Category
instance.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With