Haskell: I don't understand how arrows can be used?

I wrote a toy code to play with the concept of "Arrows". I wanted to see if I could write an arrow that encoded the concept of a state function - giving a different value after different calls.

{-# LANGUAGE Arrows#-} module StatefulFunc where import Control.Category import Control.Arrow newtype StatefulFunc ab = SF { unSF :: a -> (StatefulFunc ab, b) } idSF :: StatefulFunc aa idSF = SF $ \a -> (idSF, a) dotSF :: StatefulFunc bc -> StatefulFunc ab -> StatefulFunc ac dotSF fg = SF $ \a -> let (g', b) = unSF ga (f', c) = unSF fb in (dotSF f' g', c) instance Category StatefulFunc where id = idSF (.) = dotSF arrSF :: (a -> b) -> StatefulFunc ab arrSF f = ret where ret = SF fun fun a = (ret, fa) bothSF :: StatefulFunc ab -> StatefulFunc a' b' -> StatefulFunc (a, a') (b, b') bothSF fg = SF $ \(a,a') -> let (f', b) = unSF fa (g', b') = unSF ga' in (bothSF f' g', (b, b')) splitSF :: StatefulFunc ab -> StatefulFunc ab' -> StatefulFunc a (b, b') splitSF fg = SF $ \a -> let (f', b) = unSF fa (g', b') = unSF ga in (splitSF f' g', (b, b')) instance Arrow StatefulFunc where arr = arrSF first = flip bothSF idSF second = bothSF idSF (***) = bothSF (&&&) = splitSF eitherSF :: StatefulFunc ab -> StatefulFunc a' b' -> StatefulFunc (Either a a') (Either b b') eitherSF fg = SF $ \e -> case e of Left a -> let (f', b) = unSF fa in (eitherSF f' g, Left b) Right a' -> let (g', b') = unSF ga' in (eitherSF f g', Right b') mergeSF :: StatefulFunc ab -> StatefulFunc a' b -> StatefulFunc (Either a a') b mergeSF fg = SF $ \e -> case e of Left a -> let (f', b) = unSF fa in (mergeSF f' g, b) Right a' -> let (g', b) = unSF ga' in (mergeSF f g', b) instance ArrowChoice StatefulFunc where left = flip eitherSF idSF right = eitherSF idSF (+++) = eitherSF (|||) = mergeSF 

So, after I looked at the various definitions of type classes (not sure what or how ArrowZero will work for this, so I skipped it), I defined some helper functions

 evalSF :: (StatefulFunc ab) -> a -> b evalSF fa = snd (unSF fa) givenState :: s -> (s -> a -> (s, b)) -> StatefulFunc ab givenState sf = SF $ \a -> let (s', b) = fsa in (givenState s' f, b) 

And developed an example of use

 count :: StatefulFunc a Integer count = givenState 1 $ \c _ -> (c+1, c) countExample :: StatefulFunc a Integer countExample = proc _ -> do (count', one) <- count -< () (count'', two) <- count' -< () (count''', three) <- count'' -< () returnA -< three 

However, when I try to compile countExample , I get "Not to scale" errors for count' and count'' , which I suppose means that I need to go back to the tutorial and read what can be used when. I think I will like it anyway

 countExample :: Integer countExample = let (count', one) = unSF count () (count'', two) = unSF count' () (count''', three) = unSF count'' () in three 

But it was awkward, and I was hoping for something more natural.

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?

+17
haskell arrows
Sep 10 '10 at 15:52
source share
1 answer

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.

+29
Sep 10 2018-10-10T00:
source share



All Articles