6

I'm still trying to get a hang of the parallels between arrow notation and the semantics of the Arrow typeclasses defined in Haskell. In particular, this question seems to have a very canonical example of a small counter written with arrow notation:

counter :: ArrowCircuit a => a Bool Int
counter = proc reset -> do
        rec     output <- returnA -< if reset then 0 else next
                next <- delay 0 -< output+1
        returnA -< output

Can someone show me how to convert this back into Haskell2010 without arrow notation?

Community
  • 1
  • 1
Mokosha
  • 2,567
  • 14
  • 30
  • This book could help a bit: http://www.soi.city.ac.uk/~ross/talks/fop.pdf and this: http://www.soi.city.ac.uk/~ross/papers/notation.pdf – viorior Aug 12 '14 at 08:01
  • @viorior What was the name of the book that could help understanding arrow notation? The link is now dead. Never mind. I think I found it here: http://staff.city.ac.uk/~ross/papers/fop.ps.gz – Michael Fox Nov 30 '14 at 20:08

2 Answers2

11
{- |
                     +---------+
 >Bool>-------------->         |
                     |         >------------------>Int>
       +---------+   |  arr f  |
  /----> delay 0 >--->         >---------\
  |    +---------+   |         |         |
  |                  +---------+         |
  |                                      |
  \--------------------------------------/ 

 -}
counter' :: ArrowCircuit a => a Bool Int
counter' = loop $ second (delay 0) >>> arr f
  where
    f (reset, next) = let output = if reset then 0 else next
                          next' = output + 1
                       in (output, next')

The recursive rec part is implemented using loop. The inner part that converts reset to output using next (and producing new next value) is just a pure function with two inputs and two outputs.

Petr
  • 60,177
  • 8
  • 136
  • 295
  • Thanks, the diagram helps. Do we not end up in a `<>` here because the delay will always stall the required `next` value? – Mokosha Aug 12 '14 at 13:17
  • @Mokosha Exactly. Without the `delay`, the circuit would try to compute the fixed point of `output = output + 1` (unless `reset` would be `True`) and diverge. – Petr Aug 12 '14 at 15:37
3

A parallellism in functional code, would be to use a state op. in a fold

import Data.List

counter :: (Int, Int) -> Bool -> (Int, Int)
counter (_, previous_next) reset  =
   let output = if reset then 0 else previous_next
       next = output +1
   in (output, next)

runCounter :: [Bool] -> (Int, Int) 
runCounter = foldl' counter (0,1)

main = do
   let resets = [True, False, True, False, False]   
       result = fst $ runCounter resets
   print result 
Gabriel Riba
  • 6,500
  • 2
  • 15
  • 19
  • or `runCounter :: [Bool] -> [Int]; runCounter = tail . map fst . scanl counter (undefined,0)` (for the `delay 0`, right?) and `result = last $ runCounter resets`. – Will Ness Aug 17 '14 at 08:33