A's point of view is a bit odd - generally speaking, an abstraction will not be stronger and more general than any other abstraction; these two contradict each other. Having “more power” means knowing more about the structure of what you are working with, which means more limitations. On the one hand, you know exactly what type you are working with. It is extremely powerful; you can apply any valid function to it. On the other hand, it is also not general in general: the code written with this assumption is applicable only to this type! On the other hand, you know nothing about your type (for example, with a variable of type a ). This is very general, applicable to each type, but also not very powerful, because you do not have enough information to do anything at all!
An example of a larger root in real code is the difference between Functor and Applicative . Here Functor is more general - strictly more Functor types than Applicative s, since each Applicative also a Functor , but not vice versa. However, since Applicative has a larger structure, it is strictly more powerful. With Functor you can only display functions with one argument over your type; with Applicative you can display functions of any number of arguments. Again: one is more general, the other is more powerful.
So what is this? Are arrows more powerful or more general than monads? This is a more complicated question than comparing functors, applicative functors and monads, because arrows are a completely different beast. They even have a different look: monads and others. There is a look * -> * , where the arrows look like * -> * -> * . Fortunately, it turns out that we can identify arrows with applicative functors / monads, so we can really answer this question: arrows are more general than monads, and therefore less powerful. Given any monad, we can build an arrow out of it, but we cannot build a monad for each arrow.
The basic idea is as follows:
instance Monad m => Category (a -> mb) where id = return (f . g) x = gx >>= f instance Monad m => Arrow (a -> mb) where arr f = return . f first f (x, y) = fx >>= \ x' -> return (x', y)
However, since we have an arrow instance for a -> b , we have to wrap a -> mb in newtype in real code. This newtype is called Klesli (due to the Klesli categories ).
However, we cannot go the other way - there is no construction to get Monad from any Arrow . This is because Arrow computation cannot change its structure based on the values flowing through it, while monads can. The only way around this is to add strength to your arrow abstraction with some extra primitive function; this is exactly what ArrowApply does.
The >>> operator for arrows is a generalization of a function . for functions and, therefore, has the same general limitations. >>= , on the other hand, is more like a generalization of a function application. Pay attention to the types: for >>> , both sides are arrows; for >>= first argument is the value ( ma ), and the second is the function. Moreover, the result >>> is another arrow, the result of which is the result >>= . Since arrows have only >>> , but are not equivalent to >>= , you cannot "apply" them to arguments at all - you can only create pipelines for pipelines. The actual apply / run function must be specific to any given arrow. Monads, on the other hand, are defined in terms of >>= , and therefore come with some default app concept.
ArrowApply simply extends the arrows with the app , which is the general concept of the application. Imagine a normal application:
apply :: (b -> c) -> b -> c apply fx = fx
we can parse this:
apply :: ((b -> c), b) -> c
The way to generalize arrow functions is basically to replace -> with variable ( a ). Let's do it for apply , replacing both occurrences -> infix a :
apply :: (b `a` c, b) `a` c
We can still see the same structure as the first version of apply , just uncurried and `a` instead of -> . Now, if we just get rid of backticks and prefix a , we get a signature for the app :
app :: a (abc, b) c
So we see how ArrowApply just adds some concept of the application to the arrows. This is a parallel with >>= , which is the concept of an application for monads (or, in particular, functions of the form a -> mb ). This is quite an additional structure for building a monad from an arrow, so ArrowApply is isomorphic to Monad .
Why will we ever want to use them? Honestly, I don’t think we will do it. The arrows are quite overpriced, so stick with monads and applicative functors.