如何使用 State monad 重构代码以通过添加更多函数来增加模块化?
How to refactor code using the State monad in order to increase modularity by adding more functions?
我正在尝试掌握 monad 的窍门。我认为使用 state 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
我如何将 when
逻辑移动到它们自己的函数中?我一直在玩这个,但镜头似乎引起了问题。
Could not deduce Control.Monad.State.Class.MonadState from .=
我认为有一个功能here
我失踪了。
我想做类似
的事情
newFun = when (not _rx_busy && _rx_d2 == 0) $ do
rx_busy .= True
rx_sample_cnt .= 1
rx_cnt .= 0
我认为我需要像 execState
这样的功能。
所以我的问题是,
- 我应该使用什么?
- 该函数的作用是什么使其可以与其他函数组合?
- 如果 monad 只是组合,为什么我不能在没有 execState 类型函数的函数中使用
when
?
- 还有哪些其他方法可以提高此代码的模块化程度?
在你的定义中newFun
newFun = when (not _rx_busy && _rx_d2 == 0) $ do
rx_busy .= True
rx_sample_cnt .= 1
rx_cnt .= 0
您正在尝试引用 _rx_busy
和 _rx_d2
,它们是 RxReg
类型的记录字段。在 uartRX
中,第一个参数是 RxReg
,RecordWildCards 语言扩展用于通过将参数与模式 RxReg{..}
匹配来绑定所有字段名称。所以你要么需要传递 r
本身,要么至少传递 _rx_busy
和 _rx_d2
,从 uartRX
到 newFun
;即有
newFun busy d2 = when (not busy && d2 == 0) $ do
...
或
newFun RxReg{..} = when (not _rx_busy && _rx_d2 == 0) $ do
...
我正在尝试掌握 monad 的窍门。我认为使用 state 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
我如何将 when
逻辑移动到它们自己的函数中?我一直在玩这个,但镜头似乎引起了问题。
Could not deduce Control.Monad.State.Class.MonadState from .=
我认为有一个功能here
我失踪了。
我想做类似
的事情newFun = when (not _rx_busy && _rx_d2 == 0) $ do
rx_busy .= True
rx_sample_cnt .= 1
rx_cnt .= 0
我认为我需要像 execState
这样的功能。
所以我的问题是,
- 我应该使用什么?
- 该函数的作用是什么使其可以与其他函数组合?
- 如果 monad 只是组合,为什么我不能在没有 execState 类型函数的函数中使用
when
? - 还有哪些其他方法可以提高此代码的模块化程度?
在你的定义中newFun
newFun = when (not _rx_busy && _rx_d2 == 0) $ do
rx_busy .= True
rx_sample_cnt .= 1
rx_cnt .= 0
您正在尝试引用 _rx_busy
和 _rx_d2
,它们是 RxReg
类型的记录字段。在 uartRX
中,第一个参数是 RxReg
,RecordWildCards 语言扩展用于通过将参数与模式 RxReg{..}
匹配来绑定所有字段名称。所以你要么需要传递 r
本身,要么至少传递 _rx_busy
和 _rx_d2
,从 uartRX
到 newFun
;即有
newFun busy d2 = when (not busy && d2 == 0) $ do
...
或
newFun RxReg{..} = when (not _rx_busy && _rx_d2 == 0) $ do
...