Can someone explain how I don’t understand how the arrows work and how they can be used? Is there a fundamental philosophy for arrows that I miss?
It seems to me that you treat this Arrow as a Monad . I do not know if this is considered a "fundamental philosophy", but there is a significant difference between them, despite how often they intersect. In a way, the key element that defines a Monad is the join function; how to collapse a nested structure into one layer. They are useful because join allows you to: you can create new monadic layers in a recursive function, change the structure of the Functor based on its contents, etc. But this is not about Monad s, so we will leave it to that.
The essence of Arrow , on the other hand, is a generic version of a function . A class of type Category defines generalized versions of the composition of functions and identification functions, and a class of type Arrow determines how to raise a regular function to Arrow and how to work with Arrow , which take several arguments (in the form of tuples - Arrows does not have to be made!).
When combining Arrow basic way, like in your first countExample function, all you really do is something like a complex composition of functions . Take a look at your definition (.) - you take two functions with a state and combine them into one function with a state, while the behavior of a state change is processed automatically.
So, the main problem with your countExample is that it even mentions count' and the like. This is all done behind the scenes, just as you don't need to explicitly pass the state parameter when using the do notation in the State monad.
Now, since the proc notation just allows you to build a large composite Arrow s to actually use your state function, you will need to work outside the Arrow syntax, like you need runState or one to actually perform the calculation in the State monad. Your second countExample is along these lines, but too specialized. In the general case, your state function maps the input stream to the output stream, making it the final state converter , so runStatefulFunction will probably take a lazy list of input values and convert them to a lazy list of output values, using the right speed with unSF to feed them one at a time converter.
If you want to see an example, the Arrows package includes an Arrow Automaton transformer that defines something almost identical to your StatefulFunction , with the exception of an arbitrary Arrow instead of the simple function you used.
Oh and briefly review the relationship between Arrow and Monad s:
Plain Arrows are just first-order functions. As I said, they cannot always be curries, and they cannot always be “applied” in the same sense that the function ($) applies functions. If you really want a higher order Arrows , a class like ArrowApply defines an Arrow application. This adds a lot of power to Arrow and, among other things, allows you to use the same “collapse nested structure” function as Monad , which allows you to generally define a Monad instance for any ArrowApply instance.
In the other direction, since Monad allows you to combine functions that create a new monadic structure, for any Monad m you can talk about the “Claysley arrow”, which is a function like a -> mb . The Kleisli shooters for a Monad can be given an instance of Arrow rather obvious way.
Apart from ArrowApply and ArrowApply arrows, there are no particularly interesting relationships between class types.