Question

What is the advantage of arrows over regular functions in haskell. What can they do the functions can't. Functions can map over structures using fmap.

Was it helpful?

Solution

On more of a broad picture, arrows get you out of Hask and into other categories there are to explore. The Kleisli category is probably the best-acquainted to Haskellers, followed by Cokleisli. Those are the natural "extensions" of Hask: add an endofunctor around either the result or argument, then you get a category again if

  • Kleisli: the functor is a monad, so id ≅ return :: a -> m a

    (.) ≅ (<=<) :: (b->m c) -> (a->m b) -> a->m c
    
  • CoKleisli: the functor is a comonad, so id ≅ coreturn :: m a -> a and

    (.)     ::     (m b->c) -> (m a->b) -> m a->c
    

(For that you don't need Arrow yet, only Category. But general categories aren't very interesting, you normally want monoidal or even cartesian closed categories, which is what Arrow is roughly aiming at.)

But there are sure are lots of other categories. Most don't have much to do with Hask and can't be expressed with the standard Arrow class, mainly because the objects have special properties that not every Haskell type fulfills. Actually, if you add the ability to constrain the object types, the possibilities immediately become much wider. But even if you stay with the standard classes, perhaps even simply in ->, the point-free composition-style that is natural with arrows often comes out very nice, concise, and opens up new ways to think about transformations.

OTHER TIPS

Functions are only an instance of arrows, it's like asking "Why use monads instead of just Maybe".

Anything you can do with arrows can of course be done with functions since the Arrow (->) instance can only talk about one small part of functions, namely what's in the Arrow type class. However, arrows has more instances than just plain functions, so we can use the ssame functions to operate on more complex types.

Arrows are nice since they can have a lot more structure than just a function, when traversing with just fmap, we have no way to accumulate effects, are more expressive than monads! Consider the Kleisli arrow,

newtype Kleisli m a b = Kleisli {runKleisli :: a -> m b}

This forms an arrow when m is a monad. So every Monad forms an arrow and thus we can build up monadic computations by seamlessly composing a -> m b's and do all sorts of useful things like this. Some XML libraries use arrows to abstract over functions from an element to it's subelements and use this to traverse over the document. Other parsers use arrows (their original purpose) though nowadays this seems to be falling out of favor for Applicative.

The point that you've hopefully noticed is that arrows are more generic, when we just talk about arrows, we avoid duplicating all the code that we would need to write to do something with our parsers, xml scrapers, and monadic functions!

It's just the same as opting for Monad over Maybe, we lose some power since we're no longer able to make specific statements, but we get more generic code in return.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top