2012-01-31 83 views
3

我正在处理此问题,并且以前曾询问过相关问题。 Implementation of State Monad为了进一步优化我的代码,我试图只使用一个增量函数来实现它。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 

如何删除这个错误?

回答

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中以这种方式定义的记录。我们从Control.Monad.State隐藏 modify,以避免碰撞。

你可以看一下在 documentation for PureM 与状态单子使用其他功能的其他 功能, getsputs

如果你没有安装fclabels,但你必须从cabal-install包(如果安装Haskell的平台,你领)cabal可执行文件,你可以通过简单地运行安装fclabels

cabal install fclabels 

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

cabal update 
+0

模块Data.Label.PureM是不存在的库。我如何安装它? – 2012-01-31 12:01:11

+0

更新了我的答案,并附上一些说明如何安装'fclabels',它可以为cabal提供'Data.Label.PureM'。 – danr 2012-01-31 13:08:51

+1

+1镜片(或“标签”或任何你喜欢称之为的)的完美卖点, – 2012-01-31 15:36:18