我正在研究这个问题,之前曾问过相关问题。 Implementation of State 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
但我在这里收到错误
`x' is not a (visible) constructor field name
如何删除此错误?
答案 0 :(得分:8)
你在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
这里有一些神奇的部分。我们使用相同的GlobState
定义
记录名称,但前缀为下划线。然后功能
mkLabels
使用TemplateHaskell
为每个字段定义“镜头”
在记录中。这些镜头将具有相同的名称,但没有下划线。 (GlobState :-> Int)
到incr
的论证是这样的
镜头,我们可以使用modify
中的Data.Label.PureM
函数
它更新了在状态monad中以这种方式定义的记录。我们藏起来
来自modify
的{{1}},以避免碰撞。
你可以看另一个
功能
documentation for
PureM
对于可用于状态monad的其他函数,如
Control.Monad.State
和gets
。
如果您没有安装puts
,但是您拥有fclabels
包中的cabal
可执行文件(如果您安装了Haskell平台,则会得到),您可以安装{{ 1}}只需运行:
cabal-install
如果这是您第一次运行fclabels
,则首先需要更新数据库:
cabal install fclabels