1

I would like to represent a circuit with the following behaviour in Haskell:

The circuit has two inputs :

  • a data input
  • and a switch input

and one output.

  • When the switch = True then output t = input t
  • when switch = False, output t = output t-1

I need a way to represent the feedback loop, and the state.

I know there is several libraries providing abstractions for this sort of thing, but they look like magic to me.

Is there a simple way to model this?

Edit:

type Signal a = [a]

type Input   = Signal Int
type Output  = Signal Int
type State   = Int
type Switch = Signal Bool

delay :: a -> Signal a -> Signal a
delay = (:)

circuit :: State -> Input -> Switch -> Output
circuit s (i:is) (True:bs) =  i : circuit i is bs
circuit s (i:is) (False:bs) = s : circuit s is bs

I used streams to represent the signals, and I carry the state around explicitly in s. But what if I wanted to extract (look at) the elements in the output stream on the fly?

It looks like a problem that the State Monad would solve but I can't find a way to represent it.

fayong lin
  • 214
  • 2
  • 15
  • 2
    on first glance this seems to do the trick: `circuit switch input t = if switch then input t else input (t-1)` ... it has 2 inputs, and it should obey your laws (of course I assume that the last part `output (t) = output (t-1)` is a typo) – Random Dev Nov 18 '15 at 08:07
  • @Carsten, oh, of course, very elegant! – luqui Nov 18 '15 at 08:10
  • @Carsten, it is not a typo, I want my circuit to return at a given time it's previous output if the switch is off, and the current input (which then will become it's new stored value) when the switch is on. The difficulty comes from the need of a feedback loop and for a way to store a state. – fayong lin Nov 18 '15 at 08:18
  • Sorry I misunderstood your `t` (which obviously stand for time) - but alas you have an great answer already :D – Random Dev Nov 18 '15 at 09:03
  • I'm not sure what you mean by "on the fly". You mean you have some real time input? Fwiw this is what FRP was invented for. – luqui Nov 18 '15 at 22:29
  • Actually it's looking into Yampa that gave me the idea for this. The interface it provides is very nice and indeed seems well suited for this sort of thing, but to me the implementation is a black box. I wanted to get an intuition about how to build this kind of thing. For now I think the model I built below using the State monad gives me the degree of flexibility I want (the state is available to look at, and different "circuits" can be combined together into a stateful computation), while staying at a level of abstraction simple enough that I can understand the underlying plumbing. – fayong lin Nov 19 '15 at 02:40
  • I will definetely keep looking into FRP though. – fayong lin Nov 19 '15 at 02:40

2 Answers2

7

For what it's worth: this problem is a very good fit for some of Haskell's unique and powerful abstractions such as Monad and Arrow. But those take time and experience to learn, and I'm guessing by your wording that you aren't very familiar with them. If you stay with Haskell, I suggest revisiting this problem again in the future for an enlightenment. I will not spend more time on such things.

You can model a discrete time-varying signal simply by an infinite list:

type Signal a = [a]

zeroS, oneS :: Signal Bool
zeroS = repeat False
oneS  = repeat True

You can combine signals pointwise using zipWith. For example:

andS :: Signal Bool -> Signal Bool -> Signal Bool
andS = zipWith (&&)

ghci> andS zeroS oneS
[False,False,False,False,False,False,False,False,...

You can delay a signal by 1 unit of time by consing on the beginning of the list. This requires an initial value for the signal to take.

delay :: a -> Signal a -> Signal a
delay x s = x : s
-- or just
-- delay = (:)

In your problem you describe a function with this type:

circuit :: Signal Bool -> Signal Bool -> Signal Bool
circuit switch input = ...

which you can build using these concepts and recursion. Good luck!

luqui
  • 59,485
  • 12
  • 145
  • 204
0

Ok I think I finally got how to use the State Monad for this.

Import Control.Monad.State

type Stored a = a
type Input a = a
type Output a = a
type Switch = Bool

circuit :: Input a -> Switch -> State (Output a) (Stored a)
circuit input switch = 
    do store <- get
       if switch
       then put input >> return input
       else return store

example1 = do circuit 0 False
              circuit 2 False
              circuit 3 False

res1 = runState example1 $ 4

loopCircuit :: [(Input a, Switch)] -> State (Output a) [(Stored a)]
loopCircuit [] = return []
loopCircuit ((i,s):xs) = 
    do v  <- circuit i s 
       vs <- loopCircuit xs
       return (v:vs)

example2 = runState (loopCircuit [(0,True),(2,False),(3,True)]) 4

The circuit function can work on discrete inputs, while loopCircuit can work on a stream of inputs.

res1 = (4,4)
example2 = ([0,0,3],3)
fayong lin
  • 214
  • 2
  • 15