Convert from arrow notation
Asked Answered
E

2

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?

Equipotential answered 12/8, 2014 at 4:24 Comment(2)
This book could help a bit: soi.city.ac.uk/~ross/talks/fop.pdf and this: soi.city.ac.uk/~ross/papers/notation.pdfRobtrobust
@Robtrobust 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: staff.city.ac.uk/~ross/papers/fop.ps.gzViquelia
S
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.

Sawyer answered 12/8, 2014 at 8:44 Comment(2)
Thanks, the diagram helps. Do we not end up in a <<loop>> here because the delay will always stall the required next value?Equipotential
@Equipotential Exactly. Without the delay, the circuit would try to compute the fixed point of output = output + 1 (unless reset would be True) and diverge.Sawyer
T
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 
Theis answered 12/8, 2014 at 13:55 Comment(1)
or runCounter :: [Bool] -> [Int]; runCounter = tail . map fst . scanl counter (undefined,0) (for the delay 0, right?) and result = last $ runCounter resets.Cryptogenic

© 2022 - 2024 — McMap. All rights reserved.