如何模拟haskell状态?

时间:2013-03-12 08:48:01

标签: haskell state

我写了一些haskell代码来切换覆盆子pi上的一个引脚,这取决于我从raspberry pi上的另一个引脚获得的中断。我根本不知道如何在不知道之前的切换状态的情况下切换引脚的状态。该程序本身非常简单。

 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

所以基本上这里发生的是引脚7被注册为中断。只要引脚7从高电平变为低电平或从低电平变为高电平,就会触发中断。无论何时触发中断,它都会调用onoff函数来切换引脚0的状态。

main功能正确无误。问题是onoff函数。 onoff函数的所需行为是当引脚为低电平时将引脚0置为高电平,并在引脚为高电平时将引脚置为低电平。但要做到这一点,我需要在之前的onoff调用中存储引脚的先前状态。

我试过状态monad。但问题是状态monad基于初始状态值传递状态。但在随后的onoff调用中,似乎无法更改初始状态值。我想到了IORef,它似乎没有什么不同。它似乎正在做状态正在做...但只在IO内部。

我可以清楚地看到我非常缺乏在全局变量中存储状态的能力。而且我很高兴我无法做到这一点,因为我知道还有其他一些惯用的方法可以达到同样的目标。

非常感谢任何正确方向的帮助。

干杯和问候。

2 个答案:

答案 0 :(得分:7)

State monad实际上是将一个额外参数传递给你的函数的想法的抽象 - 它仍然是纯粹的,它只是给你很多语法帮助。另一方面,IORef是实际可变值的实际更新,这就是它必须存在于IO monad中的原因。除非出于性能原因的要求,否则这通常被认为是不合需要的,因为您丢失了纯代码中有关惰性和执行顺序以及并发性的所有承诺。

使用StateIO可以通过使用StateT monad转换器来实现,该转换器可能被认为是围绕IO monad包装State monad。 Haskell wiki上有一些例子:http://www.haskell.org/haskellwiki/Simple_StateT_use显示了如何在使用I / O时维护状态,以及如何使用lift来使IO monad函数在StateT内运行。< / p>

答案 1 :(得分:1)

这是一个小例子。我不确定它是否是惯用的Haskell,但它应该足以让你走上正确的轨道。它不是实际切换一个引脚(我没有Raspberry Pi来测试),而是打印状态。它们都是IO(),但它应该匹配。

你的真实状态可能是一个记录/列表/阵列的引脚。然后,您将索引传递给togglePin,它将具有类似

的类型
togglePin :: Int -> PStateT

无论如何 - 这是一个例子,它在这里编译并运行良好。

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 ()