Possible Arrow typeclasses?

class ArrowUncurry a where 
        uncurry :: (r -> a b c) -> a (r,b) c

class ArrowFlip a where 
        flip :: (r -> a b c) -> a b (r -> c)

Class names based off of their implementation for ->. Are either of these definable using existing arrow typeclasses? Can they be built with something from profunctors?

2 Likes

ArrowApply is sufficient for uncurry:

uncurry :: ArrowApply a => (r -> a b c) -> a (r, b) c
uncurry f = first (arr f) >>> app

(And the converse is true as well; given an uncurry, you have app = uncurry id, so ArrowUncurry and ArrowApply are equivalent.)

I don’t know about flip, at least as you’ve generalized it. Commuting a and (->) like that strikes me as a fairly un-Arrow-ish operation. It’s not something you could implement for Kleisli, for example; that would be equivalent to:

flipM :: Monad m => (r -> b -> m c) -> b -> m (r -> c)

and I’m pretty sure there’s no safe implementation of that.

However, a different generalization that leaves the arrow types uncommuted is an easy corollary of uncurry:

flip :: ArrowApply a => (r -> a b c) -> b -> a r c
flip a b = arr (, b) >>> uncurry a
2 Likes

Right, and ArrowApply is basically equivalent to Monad. I think ArrowFlip is equivalent to Monad too, see here: http://web.jaguarpaw.co.uk/\~tom/blog/posts/2012-09-02-what-is-a-monad-really.html [EDIT: ah no, that’s just ArrowUncurry again]

But you can get a >>= like operation out of ArrowFlip like this:

(a b r, r -> a b c) [>>= arguments]
(a b r, a b (r -> c)) [use flip]
a b c [apply Arrow equivalent of <*>]

I haven’t double checked, but doesn’t this make it equivalent to at least as powerful as ArrowApply/Monad?

Yes, it’s a very powerful operation. Thinking aloud: is it actually equivalent to Representable?

I don’t think it’s quite that powerful; ArrowFlip doesn’t witness a full isomorphism with (->). Going the one way, if we have Representable (a b), then flip = distribute. But going the other way, implementing index :: a _ c -> Rep (a _) -> c is a problem no matter how you fill in the blanks; getting a c out requires a way to actually evaluate the arrow, which, just like getting values out of a Monad, rather defeats the point of that particular abstraction. Even ArrowFlip doesn’t give you that.

These are special cases of what category theorists call copowering and powering respectively, although then uncurry and flip should be isomorphisms. To make this really useful, you wouldn’t want the copower to be fixed to (,) nor the power fixed to (->).

I have powering and copowering in my proarrow library with a few instances, but only the instance for (->) uses these types.

Powers are right adjoint to cartesian products. The generalisation of this are monoidal closed categories.
The archetype of arrow category are the Kleisli categories. These preserve the cartesian coproduct of the base category, but in general not the product. Hence it is rare that a Kleisli category has products, let alone powers. Example: These is the cartesian product in Kleisli Maybe.

I think a better place to explore these concepts is when we free ourselves from (,) as the product operation, as Arrow dictates.

It is implementable on CoKliesli however. I was wondering if there was a relationship between the two.