Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to simulate haskell state?

Tags:

haskell

state

I wrote some haskell code to toggle a pin on the raspberry pi depending on an interrupt I get from another pin on the raspberry pi. I simply do not know how to toggle the state of the pin without knowing the previous toggle state. The program itself is very simple.

 import Control.Concurrent
 import Data.IORef
 import HasberryPi


 main = do wiringPiSetup
           pinMode 0 output
           pinMode 7 input 
           pullUpDnControl 7 pull_down
           wiringPiISR 7 edge_both onoff
           threadDelay (15*(10^6))

 onoff s = do a <- readIORef s                               -- This is wrong
              digitalWrite 0 (if b then pinhigh else pinlow) -- This is wrong

So basically what happens here is pin 7 is registered as an interrupt. The interrupt is triggered whenever pin 7 goes from high to low or low to high. And whenever the interrupt is triggered it calls the onoff function that toggles the state of pin 0.

The main function is correct. Its the onoff function that is the problem. The wanted behavior of the onoff function is to make pin 0 high when the pin it is low and toggle the pin low when it is high. But to do that I need to store the previous state of the pin in the previous call to onoff.

I tried the state monad. But the problem is that the state monad passes state around based on an initial state value. But in subsequent calls to onoff it seems impossible to change the initial state value. I thought about IORef and it seems no different. It just seems like it's doing what state is doing.. but only inside the IO.

I can clearly see that I am sorely missing the ability of storing state in a Global variable. And I'm happy that I'm not able to do it because I know there is some other idiomatic way of achieving the same goal.

Any help in the right direction is very much appreciated.

Cheers and Regards.

like image 290
Jay Avatar asked Mar 12 '13 08:03

Jay


2 Answers

The State monad is really an abstraction over the idea of passing state around in an extra parameter to your functions - it's still pure, it just gives you a lot of syntactic help. IORef, on the other hand, is an actual update of an actual mutable value, which is why it has to live inside the IO monad. This is generally considered undesirable unless required for performance reasons, as you lose all the promises you get with pure code about laziness and order of execution and concurrency.

Using State and IO together is achieved by use of the StateT monad transformer, which might be thought of as wrapping the State monad around the IO monad. There are some examples on the Haskell wiki: http://www.haskell.org/haskellwiki/Simple_StateT_use which shows how you maintain state while using I/O, and how to use lift to get IO monad functions to run inside StateT.

like image 104
Matthew Walton Avatar answered Oct 22 '22 21:10

Matthew Walton


Here's a small example. I'm not sure if it's idiomatic Haskell but it should be enough to get you on the right track. Rather than actually toggling a pin (I don't have a Raspberry Pi to test against) it just prints the state. They're both IO () though so it should match.

Your real state would presumably be a record/list/array of pins. You'd then pass in an index to togglePin and it would have a type like

togglePin :: Int -> PStateT

Anyway - here's the example, it compiles and runs fine here.

import Control.Monad.State

-- Presumably you've got something like this defined in a library
data Pin = PinHigh | PinLow
    deriving (Eq,Show)

-- A simple state would be
-- type PState = State Pin
-- We want to wrap our state around IO () so need a transformer
type PStateT = StateT Pin IO ()

-- Simple print function
printPinState :: String -> Pin -> IO ()
printPinState msg pin = putStrLn $ msg ++ (show pin)

-- Toggles the state, real function would set the pin's level too rather than 
-- just print it's new state
togglePin :: PStateT
togglePin = do
    curr_p <- get
    lift $ printPinState "toggle before: " curr_p
    let new_p = if curr_p == PinHigh then PinLow else PinHigh
    lift $ printPinState "toggle after:  " new_p
    put new_p
    return ()

-- Initialise our state, then run our toggle function using the state
-- as its environment.
main = do
    let env = PinLow
    printPinState "main before:   " env
    (_, env') <- runStateT (togglePin) env
    printPinState "main after:    " env'
    -- And again for luck...
    (_, env'') <- runStateT (togglePin) env'
    return ()
like image 1
Richard Huxton Avatar answered Oct 22 '22 21:10

Richard Huxton