我正在研究这个问题,之前曾问过相关问题.状态Monad的实现为了进一步优化我的代码,我试图仅使用一个增量函数来实现它.
module StateExample where
import Control.Monad.State
data GlobState = GlobState { c1 :: Int, c2:: Int, c3:: Int} deriving (Show)
newGlobState:: GlobState
newGlobState = GlobState { c1=0,c2=0,c3=0 }
incr :: String-> State GlobState ()
incr x = do
modify(\g -> g {x =x g + 1})
main:: IO()
main = do
let a1= flip execState newGlobState $ do
incr c1
incr c2
incr c1
print a
Run Code Online (Sandbox Code Playgroud)
但在这里我得到一个错误
`x' is not a (visible) constructor field name
Run Code Online (Sandbox Code Playgroud)
我该如何删除此错误?
你在Haskell中遇到了一个弱点:记录不是一流的价值!实际上,像你所做的那样写作会很好,但这是不可能的.但是,您可以使用不同的库来实现所需的效果.如果您使用fclabels,它就是这样的 :
{-# LANGUAGE TemplateHaskell, TypeOperators #-}
module StateExample where
import Control.Monad.State hiding (modify)
import Data.Label (mkLabels)
import Data.Label.Pure ((:->))
import Data.Label.PureM
data GlobState = GlobState { _c1 :: Int , _c2 :: Int , _c3 :: Int } deriving Show
$(mkLabels [''GlobState])
newGlobState:: GlobState
newGlobState = GlobState { _c1 = 0, _c2 = 0, _c3 = 0 }
incr :: (GlobState :-> Int) -> State GlobState ()
incr x = modify x (+1)
main :: IO ()
main = do
let a = flip execState newGlobState $ do
incr c1
incr c2
incr c1
print a
Run Code Online (Sandbox Code Playgroud)
这里有一些神奇的部分.我们GlobState使用相同的记录名称定义,但前缀为下划线.然后该函数
mkLabels用于TemplateHaskell为记录中的每个字段定义"镜头".这些镜头将具有相同的名称,但没有下划线.这个论点(GlobState :-> Int)来incr就是这样一个镜头,我们可以使用modify从功能Data.Label.PureM
用于更新记录中定义的状态单子里面这样.我们隐藏
modify的Control.Monad.State,以避免冲突.
你可以看一下其他功能的
文档PureM
与状态单子使用其他功能,如
gets和puts.
如果您尚未fclabels安装,但是您拥有该软件包中的cabal可执行文件cabal-install(如果您安装了Haskell平台,则可以获得),fclabels只需运行即可安装:
cabal install fclabels
Run Code Online (Sandbox Code Playgroud)
如果这是您第一次运行cabal,则首先需要更新数据库:
cabal update
Run Code Online (Sandbox Code Playgroud)