I like to think of Arrows, like Monads and Functors, as allowing the programmer to do exotic compositions of functions.
Without Monads or Arrows (and Functors), composition of functions in a functional language is limited to applying one function to the result of another function. With monads and functors, you can define two functions, and then write separate reusable code which specify how those functions, in the context of the particular monad, interact with each other and with the data which is passed into them. This code is placed within the bind code of the Monad. So a monad is one one view, just a container for reusuable bind code. Functions compose differently within the context of one monad from another monad.
A simple example is the Maybe monad, where there is code in the bind function such that if a function A is composed with a function B within a Maybe monad, and B produces a Nothing, then the bind code will ensure that the composition of the two functions outputs a Nothing, without bothering to apply A to the Nothing value coming out from B. If there were no monad, the programmer would have to write code into A to test for a Nothing input.
Monads also mean that the programmer does not need to explicitly type the parameters which each function requires into the source code - the bind function handles parameter passing. So using monads, the source code can begin to look more like a static chain of function names, rather than looking as though function A "calls" function B with parameters C and D - the code starts to feel more like an electronic circuit than a moving machine - more functional than imperative.
Arrows also connect functions together with a bind function, providing reusable functionality and hiding parameters. But Arrows can themselves be connected together and composed, and can optionally route data to other Arrows at runtime. Now you can apply data to two paths of Arrows, which "do different things" to the data, and reassemble the result. Or you can select which branch of Arrows to pass the data to, depending on some value in the data. The resulting code is even more like an electronic circuit, with switches, delays, integration etc. The program looks very static, and you should not be able to see much manipulation of data going on. There are fewer and fewer parameters to think about, and less need to think about what values parameters may or may not take.
Writing an Arrowized program mostly involves selecting off the shelf Arrows such as splitters, switches, delays and integrators, lifting functions into those Arrows, and connecting the Arrows together to form bigger Arrows. In Arrowized Functional Reactive Programming, the Arrows form a loop, with input from the world being combined with output from the last iteration of the program, so that the output reacts to real world input.
One of the real world values is time. In Yampa, the Signal Function Arrow invisibly threads the time parameter through the computer program - you never access the time value, but if you connect an integrator arrow into the program, it will output values integrated over time which you can then use to pass to other arrows.