State Monad示例问题

时间:2012-01-31 11:34:29

标签: haskell

我正在研究这个问题,之前曾问过相关问题。 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

如何删除此错误?

1 个答案:

答案 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.Stategets

如果您没有安装puts,但是您拥有fclabels包中的cabal可执行文件(如果您安装了Haskell平台,则会得到),您可以安装{{ 1}}只需运行:

cabal-install

如果这是您第一次运行fclabels,则首先需要更新数据库:

 cabal install fclabels