2012-01-31 82 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