如何使用State monad重构代码以通过添加更多函数来增加模块性?

err*_*ter 0 monads haskell record clash

我想要获得monad的支持.我认为与国家monad一起玩是个不错的做法.

使用CLaSH的UART Rx :

-- UART RX Logic
data RxReg
  = RxReg
  { _rx_reg        :: BitVector 8
  , _rx_data       :: BitVector 8
  , _rx_sample_cnt :: Unsigned 4
  , _rx_cnt        :: Unsigned 4
  , _rx_frame_err  :: Bool
  , _rx_over_run   :: Bool
  , _rx_empty      :: Bool
  , _rx_d1         :: Bit
  , _rx_d2         :: Bit
  , _rx_busy       :: Bool
  }

makeLenses ''RxReg

uartRX r@(RxReg {..}) rx_in uld_rx_data rx_enable = flip execState r $ do
  -- Synchronise the async signal
  rx_d1 .= rx_in
  rx_d2 .= _rx_d1
  -- Uload the rx data
  when uld_rx_data $ do
    rx_data  .= _rx_reg
    rx_empty .= True
  -- Receive data only when rx is enabled
  if rx_enable then do
    -- Check if just received start of frame
    when (not _rx_busy && _rx_d2 == 0) $ do
      rx_busy       .= True
      rx_sample_cnt .= 1
      rx_cnt        .= 0
    -- Star of frame detected, Proceed with rest of data
    when _rx_busy $ do
      rx_sample_cnt += 1
      -- Logic to sample at middle of data
      when (_rx_sample_cnt == 7) $ do
        if _rx_d1 == 1 && _rx_cnt == 0 then
          rx_busy .= False
        else do
          rx_cnt += 1
          -- start storing the rx data
          when (_rx_cnt > 0 && _rx_cnt < 9) $ do
            rx_reg %= replaceBit (_rx_cnt - 1) _rx_d2
          when (_rx_cnt == 9) $ do
            rx_busy .= False
            -- Check if End of frame received correctly
            if _rx_d2 == 0 then
              rx_frame_err .= True
            else do
              rx_empty     .= False
              rx_frame_err .= False
              -- Check if last rx data was not unloaded
              rx_over_run  .= not _rx_empty
  else do
    rx_busy .= False
Run Code Online (Sandbox Code Playgroud)

我如何将when逻辑移动到自己的功能?我一直在玩这个,但镜头似乎引起了问题.

Could not deduce Control.Monad.State.Class.MonadState from .=
Run Code Online (Sandbox Code Playgroud)

我觉得有一个函数在这里

我失踪了.

我想做点什么

newFun = when (not _rx_busy && _rx_d2 == 0) $ do
         rx_busy       .= True
         rx_sample_cnt .= 1
         rx_cnt        .= 0
Run Code Online (Sandbox Code Playgroud)

我认为会有一个像execState我需要的功能.

所以我的问题是,

  • 我该怎么用?
  • 该功能可以使其与其他功能组合?
  • 如果monad只是在编写为什么我when没有execState类型函数的函数?
  • 还有哪些其他方法可以增加此代码的模块性?

Cac*_*tus 5

在你的定义中 newFun

newFun = when (not _rx_busy && _rx_d2 == 0) $ do
         rx_busy       .= True
         rx_sample_cnt .= 1
         rx_cnt        .= 0
Run Code Online (Sandbox Code Playgroud)

你试图引用_rx_busy_rx_d2,这是该类型的记录字段RxReg.在uartRX,第一个参数是a RxReg,RecordWildCards语言扩展用于通过将参数与模式匹配来绑定所有字段名称RxReg{..}.所以,你要么需要通过r本身,或至少_rx_busy_rx_d2,从uartRXnewFun; 即要么

newFun busy d2 = when (not busy && d2 == 0) $ do
    ...
Run Code Online (Sandbox Code Playgroud)

要么

newFun RxReg{..} = when (not _rx_busy && _rx_d2 == 0) $ do
    ...
Run Code Online (Sandbox Code Playgroud)